Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
<key>com.apple.security.automation.apple-events</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.addressbook</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.calendars</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.automation.apple-events</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.addressbook</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.calendars</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>2BBY89MBSN.dev.warp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
OS_TYPE="$(uname -s)"
|
||||
|
||||
if [[ "$OS_TYPE" = "Darwin" ]]; then
|
||||
./script/macos/bootstrap "$@"
|
||||
elif [[ "$OS_TYPE" = "Linux" ]]; then
|
||||
./script/linux/bootstrap "$@"
|
||||
elif [[ "$OS_TYPE" =~ ^[MINGW64_NT|MSYS_NT] ]]; then
|
||||
./script/windows/bootstrap.ps1 "$@"
|
||||
else
|
||||
echo "No bootstrap script defined for the current platform!"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
OS_TYPE="$(uname -s)"
|
||||
|
||||
if [[ "$OS_TYPE" = "Darwin" ]]; then
|
||||
./script/macos/bundle "$@"
|
||||
elif [[ "$OS_TYPE" = "Linux" ]]; then
|
||||
./script/linux/bundle "$@"
|
||||
elif [[ "$OS_TYPE" =~ ^[MINGW64_NT|MSYS_NT] ]]; then
|
||||
while (( "$#" )); do
|
||||
# Turn double hyphens into single hyphens.
|
||||
PROCESSED_PARAM=$( echo $1 | sed -e "s/^--/-/" )
|
||||
case "$PROCESSED_PARAM" in
|
||||
# Powershell has a built-in argument called `debug`,
|
||||
# so we must pass that argument under a different name.
|
||||
-debug)
|
||||
# rename debug --> debug_build
|
||||
PARAMS="$PARAMS -debug_build"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
# preserve other arguments as-is
|
||||
PARAMS="$PARAMS $PROCESSED_PARAM"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
./script/windows/bundle.ps1 "$PARAMS"
|
||||
else
|
||||
echo "No bundle script defined for the current platform ($OS_TYPE)!"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Verifies that the accepted license lists in deny.toml and about.toml are in
|
||||
# sync. Exits non-zero with a diff if they diverge.
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
python3 -c "
|
||||
import tomllib, sys
|
||||
|
||||
with open('$REPO_ROOT/deny.toml', 'rb') as f:
|
||||
deny = set(tomllib.load(f)['licenses']['allow'])
|
||||
with open('$REPO_ROOT/about.toml', 'rb') as f:
|
||||
about = set(tomllib.load(f)['accepted'])
|
||||
|
||||
if deny != about:
|
||||
only_deny = deny - about
|
||||
only_about = about - deny
|
||||
if only_deny:
|
||||
print(f'In deny.toml but not about.toml: {sorted(only_deny)}')
|
||||
if only_about:
|
||||
print(f'In about.toml but not deny.toml: {sorted(only_about)}')
|
||||
sys.exit(1)
|
||||
|
||||
print('License config in sync.')
|
||||
"
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Compile a .icon bundle using actool and update the app's Info.plist
|
||||
# to support macOS Sequoia's icon tinting feature.
|
||||
#
|
||||
# Usage: compile_icon <channel> <app_bundle_path>
|
||||
# channel: The release channel (e.g., stable)
|
||||
# app_bundle_path: Path to the .app bundle (e.g., Warp.app)
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: compile_icon <channel> <app_bundle_path>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CHANNEL="$1"
|
||||
APP_BUNDLE_PATH="$2"
|
||||
|
||||
# Determine the repository root directory
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
ICON_BUNDLE_PATH="$REPO_ROOT/app/channels/$CHANNEL/icon/AppIcon.icon"
|
||||
|
||||
# Only compile if the .icon bundle exists for this channel.
|
||||
if [[ ! -d "$ICON_BUNDLE_PATH" ]]; then
|
||||
if [[ "$CHANNEL" = "oss" ]]; then
|
||||
echo "Warning: no .icon bundle found for $CHANNEL channel at $ICON_BUNDLE_PATH; skipping adaptive icon compilation." >&2
|
||||
exit 0
|
||||
fi
|
||||
echo "Error: .icon bundle not found at $ICON_BUNDLE_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Compiling .icon bundle for $CHANNEL channel"
|
||||
|
||||
BUNDLED_RESOURCES_DIR="$APP_BUNDLE_PATH/Contents/Resources"
|
||||
PARTIAL_INFO_PLIST="$(dirname "$APP_BUNDLE_PATH")/partial-icon-info.plist"
|
||||
|
||||
# Compile the .icon bundle using actool
|
||||
xcrun actool \
|
||||
--compile "$BUNDLED_RESOURCES_DIR" \
|
||||
--platform macosx \
|
||||
--minimum-deployment-target 10.14 \
|
||||
--app-icon AppIcon \
|
||||
--output-partial-info-plist "$PARTIAL_INFO_PLIST" \
|
||||
"$ICON_BUNDLE_PATH"
|
||||
|
||||
# Earlier XCode versions won't build the correct asset format for adaptive icons
|
||||
if [[ ! -f "$BUNDLED_RESOURCES_DIR/Assets.car" ]]; then
|
||||
XCODE_VERSION=$(xcodebuild -version 2>/dev/null | head -1 || echo "unknown")
|
||||
echo "Warning: actool did not produce Assets.car which is required for the latest App icon format." >&2
|
||||
echo "(Note that XCode version <26 does not support .icon bundles. Your version: $XCODE_VERSION)." >&2
|
||||
fi
|
||||
|
||||
# Update Info.plist to reference AppIcon instead of the old .icns
|
||||
plutil -replace CFBundleIconFile -string "AppIcon" "$APP_BUNDLE_PATH/Contents/Info.plist"
|
||||
plutil -insert CFBundleIconName -string "AppIcon" "$APP_BUNDLE_PATH/Contents/Info.plist" 2>/dev/null || \
|
||||
plutil -replace CFBundleIconName -string "AppIcon" "$APP_BUNDLE_PATH/Contents/Info.plist"
|
||||
|
||||
# Get the app name from the bundle
|
||||
APP_NAME=$(basename "$APP_BUNDLE_PATH" .app)
|
||||
|
||||
echo "Icon compiled successfully."
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copy channel-gated bundled skills into the app bundle.
|
||||
#
|
||||
# Usage:
|
||||
# copy_conditional_skills <channel> <gated_skills_src> <dest_skills_dir>
|
||||
#
|
||||
# Arguments:
|
||||
# channel: Release channel (local, dev, preview, stable).
|
||||
# gated_skills_src: Path to resources/channel-gated-skills/.
|
||||
# dest_skills_dir: Destination skills directory (e.g. .../bundled/skills/).
|
||||
#
|
||||
# Gating is progressive: earlier gates include all skills from later gates.
|
||||
# For example, a dogfood build includes skills from both dogfood/ and
|
||||
# preview/. The stable channel has no gate — stable-ready skills belong
|
||||
# in resources/skills/ (the always-bundled directory).
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 3 ]; then
|
||||
echo "Usage: $0 <channel> <gated_skills_src> <dest_skills_dir>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CHANNEL="$1"
|
||||
GATED_SKILLS_SRC="$2"
|
||||
DEST_SKILLS_DIR="$3"
|
||||
|
||||
# Gate labels ordered from most-inclusive to least-inclusive.
|
||||
# A channel's gate includes all gates at or after its position.
|
||||
GATE_ORDER=("dogfood" "preview")
|
||||
|
||||
if [ ! -d "$GATED_SKILLS_SRC" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Error out if a stable/ gate directory exists — stable skills should live
|
||||
# in the always-bundled resources/skills/ directory instead.
|
||||
if [ -d "$GATED_SKILLS_SRC/stable" ]; then
|
||||
echo "Error: found a 'stable/' directory in $GATED_SKILLS_SRC." >&2
|
||||
echo "The stable channel does not use gated skills. Move stable-ready skills to resources/skills/ (the always-bundled directory) instead." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Map the release channel to its gate label.
|
||||
case "$CHANNEL" in
|
||||
local|dev) GATE="dogfood" ;;
|
||||
preview) GATE="preview" ;;
|
||||
*)
|
||||
echo " Channel '$CHANNEL' has no gated skills, skipping"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Build the set of included gates (progressive: this gate and all after it).
|
||||
INCLUDED_GATES=()
|
||||
FOUND=false
|
||||
for g in "${GATE_ORDER[@]}"; do
|
||||
if [ "$g" = "$GATE" ]; then
|
||||
FOUND=true
|
||||
fi
|
||||
if [ "$FOUND" = true ]; then
|
||||
INCLUDED_GATES+=("$g")
|
||||
fi
|
||||
done
|
||||
|
||||
# Helper: check if a value is in the included gates list.
|
||||
is_included() {
|
||||
local needle="$1"
|
||||
for g in "${INCLUDED_GATES[@]}"; do
|
||||
if [ "$g" = "$needle" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Iterate over gate directories and copy matching skills.
|
||||
for gate_dir in "$GATED_SKILLS_SRC"/*/; do
|
||||
# Skip if the glob didn't match anything.
|
||||
[ -d "$gate_dir" ] || continue
|
||||
|
||||
gate_name="$(basename "$gate_dir")"
|
||||
|
||||
if ! is_included "$gate_name"; then
|
||||
# List the skills that would have been included for the skip message.
|
||||
skills=""
|
||||
for skill_dir in "$gate_dir"/*/; do
|
||||
[ -d "$skill_dir" ] || continue
|
||||
if [ -n "$skills" ]; then
|
||||
skills="$skills, "
|
||||
fi
|
||||
skills="$skills$(basename "$skill_dir")"
|
||||
done
|
||||
echo " Skipping gate '$gate_name' (channel '$CHANNEL') — would include: $skills"
|
||||
continue
|
||||
fi
|
||||
|
||||
for skill_dir in "$gate_dir"/*/; do
|
||||
[ -d "$skill_dir" ] || continue
|
||||
|
||||
skill_name="$(basename "$skill_dir")"
|
||||
dest="$DEST_SKILLS_DIR/$skill_name"
|
||||
echo " Copying gated skill: $skill_name (gate: $gate_name)"
|
||||
mkdir -p "$dest"
|
||||
cp -R "$skill_dir"/. "$dest"/
|
||||
done
|
||||
done
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
PARAMS=""
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--branch-name)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
GIT_BRANCH_NAME=$2
|
||||
shift 2
|
||||
else
|
||||
echo >&2 "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--channel)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
CHANNEL=$2
|
||||
shift 2
|
||||
else
|
||||
echo >&2 "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"
|
||||
|
||||
RELEASE_BRANCH_PREFIX="${CHANNEL}_release"
|
||||
|
||||
git fetch --all --tags
|
||||
|
||||
if [[ "$GIT_BRANCH_NAME" == "$RELEASE_BRANCH_PREFIX/"* ]]; then
|
||||
echo >&2 "Already on release branch $GIT_BRANCH_NAME, not creating new one."
|
||||
tag=$(git tag | grep "${GIT_BRANCH_NAME#"$RELEASE_BRANCH_PREFIX/"}" | sort -r --version-sort | head -n1)
|
||||
echo >&2 "Current tag on branch is $tag"
|
||||
if git rev-parse "$tag" >/dev/null 2>&1; then
|
||||
# Tag already exists, increase the RC number
|
||||
suffix=${tag: -2}
|
||||
suffix=$((10#$suffix+1))
|
||||
suffix=$(printf '%02d' $suffix)
|
||||
|
||||
tag_length=${#tag}
|
||||
non_rc=${tag:0:$(($tag_length - 3))}
|
||||
tag="${non_rc}_$suffix"
|
||||
|
||||
echo >&2 "Creating tag $tag"
|
||||
git tag "$tag"
|
||||
git push origin "$tag"
|
||||
echo "$tag"
|
||||
else
|
||||
echo >&2 "No tag found on release branch, something is wrong"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Creating release branch and new tag and version _00
|
||||
date_formatted=$(date +'%Y.%m.%d.%H.%M')
|
||||
tag="v0.$date_formatted.${CHANNEL}_00"
|
||||
echo >&2 "Creating tag $tag"
|
||||
git tag "$tag"
|
||||
git push origin "$tag"
|
||||
|
||||
release_branch_name="v0.$date_formatted.${CHANNEL}"
|
||||
echo >&2 "Creating release branch $RELEASE_BRANCH_PREFIX/$release_branch_name"
|
||||
git checkout -b "$RELEASE_BRANCH_PREFIX/$release_branch_name"
|
||||
git push -u origin "$RELEASE_BRANCH_PREFIX/$release_branch_name"
|
||||
echo "$tag"
|
||||
fi
|
||||
|
||||
export tag
|
||||
Executable
+194
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Cross-compiles the Oz CLI for Linux x86_64 (musl) on macOS and uploads it
|
||||
# to a remote host via rsync for local remote-server development.
|
||||
#
|
||||
# Uses rsync for delta transfers — after the first deploy, only changed bytes
|
||||
# are sent, which is dramatically faster for iterative development.
|
||||
#
|
||||
# Prerequisites:
|
||||
# brew install filosottile/musl-cross/musl-cross
|
||||
# rustup target add x86_64-unknown-linux-musl
|
||||
#
|
||||
# Usage:
|
||||
# script/deploy_remote_server --host user@hostname [--profile release]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Defaults
|
||||
PROFILE_MODE="dev-remote"
|
||||
HOST=""
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") --host <user@hostname> [OPTIONS]
|
||||
|
||||
Cross-compile the Oz CLI for Linux x86_64 and upload it to a remote host.
|
||||
|
||||
Required:
|
||||
--host <user@hostname> Remote host to upload to
|
||||
|
||||
Options:
|
||||
--profile <profile> Build profile: dev-remote (default, strips symbols), dev, release, or optimized
|
||||
Use --profile dev if you need symbols for remote debugging
|
||||
--help Show this help message
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--host)
|
||||
HOST="$2"
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
PROFILE_MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument: $1" >&2
|
||||
echo "Run with --help for usage." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required arguments
|
||||
if [[ -z "$HOST" ]]; then
|
||||
echo "Error: --host is required." >&2
|
||||
echo "Run with --help for usage." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate profile
|
||||
case "$PROFILE_MODE" in
|
||||
dev-remote|dev|release|optimized) ;;
|
||||
*)
|
||||
echo "Error: Unsupported profile '$PROFILE_MODE'. Use 'dev-remote', 'dev', 'release', or 'optimized'." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for musl-cross linker
|
||||
if ! command -v x86_64-linux-musl-gcc &>/dev/null; then
|
||||
echo "Error: x86_64-linux-musl-gcc not found." >&2
|
||||
echo "Install it with: brew install filosottile/musl-cross/musl-cross" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for musl target
|
||||
if ! rustup target list --installed 2>/dev/null | grep -q x86_64-unknown-linux-musl; then
|
||||
echo "Error: x86_64-unknown-linux-musl target not installed." >&2
|
||||
echo "Install it with: rustup target add x86_64-unknown-linux-musl" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine build parameters
|
||||
TARGET="x86_64-unknown-linux-musl"
|
||||
|
||||
case "$PROFILE_MODE" in
|
||||
dev-remote)
|
||||
CARGO_PROFILE="dev-remote"
|
||||
;;
|
||||
dev)
|
||||
CARGO_PROFILE="dev"
|
||||
;;
|
||||
release)
|
||||
CARGO_PROFILE="release"
|
||||
;;
|
||||
optimized)
|
||||
CARGO_PROFILE="release-lto-debug_assertions"
|
||||
;;
|
||||
esac
|
||||
|
||||
FEATURES="release_bundle,crash_reporting,standalone,agent_mode_debug"
|
||||
WARP_BIN="warp"
|
||||
BINARY_NAME="oz-local"
|
||||
REMOTE_DIR=".warp-local/remote-server"
|
||||
|
||||
# Determine the output directory
|
||||
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORKSPACE_ROOT/target}"
|
||||
case "$CARGO_PROFILE" in
|
||||
dev)
|
||||
OUTPUT_DIR="$CARGO_TARGET_DIR/$TARGET/debug"
|
||||
;;
|
||||
*)
|
||||
OUTPUT_DIR="$CARGO_TARGET_DIR/$TARGET/$CARGO_PROFILE"
|
||||
;;
|
||||
esac
|
||||
BUILT_BINARY="$OUTPUT_DIR/$WARP_BIN"
|
||||
|
||||
echo "==> Building Oz CLI for $TARGET (profile=$CARGO_PROFILE)"
|
||||
echo " Binary: $WARP_BIN -> $BINARY_NAME"
|
||||
echo " Features: $FEATURES"
|
||||
echo ""
|
||||
|
||||
# Build with linker and rustflags overrides to avoid macOS-specific flags
|
||||
# from .cargo/config.toml
|
||||
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=x86_64-linux-musl-gcc \
|
||||
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-C symbol-mangling-version=v0" \
|
||||
cargo build \
|
||||
-p warp \
|
||||
--bin "$WARP_BIN" \
|
||||
--target "$TARGET" \
|
||||
--profile "$CARGO_PROFILE" \
|
||||
--features "$FEATURES"
|
||||
|
||||
if [[ ! -f "$BUILT_BINARY" ]]; then
|
||||
echo "Error: Expected binary not found at $BUILT_BINARY" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BINARY_SIZE=$(du -h "$BUILT_BINARY" | cut -f1)
|
||||
echo ""
|
||||
echo "==> Build complete ($BINARY_SIZE)"
|
||||
|
||||
# Resolve $HOME on the remote so our upload lands in the same directory the
|
||||
# Warp client checks. The client runs `test -x ~/.warp-local/...` and lets
|
||||
# the remote login shell expand `~` to $HOME, while rsync's remote path
|
||||
# expansion can differ (e.g. on Namespace devboxes, the initial directory
|
||||
# is /workspaces but $HOME is elsewhere). Using an absolute path derived
|
||||
# from the remote $HOME keeps both sides consistent.
|
||||
REMOTE_HOME=$(ssh "$HOST" 'printf %s "$HOME"')
|
||||
if [[ -z "$REMOTE_HOME" ]]; then
|
||||
echo "Error: could not resolve remote \$HOME on $HOST" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REMOTE_ABS_DIR="$REMOTE_HOME/$REMOTE_DIR"
|
||||
|
||||
# Ensure rsync is available on the remote host
|
||||
if ! ssh "$HOST" "command -v rsync" &>/dev/null; then
|
||||
echo "==> rsync not found on remote host, installing..."
|
||||
ssh "$HOST" "sudo apt-get install -y rsync || sudo yum install -y rsync || sudo dnf install -y rsync || sudo apk add rsync"
|
||||
if ! ssh "$HOST" "command -v rsync" &>/dev/null; then
|
||||
echo "Error: failed to install rsync on $HOST. Please install it manually and re-run." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure the remote directory exists
|
||||
ssh "$HOST" "mkdir -p $REMOTE_ABS_DIR"
|
||||
|
||||
echo "==> Uploading to $HOST:$REMOTE_ABS_DIR/$BINARY_NAME"
|
||||
|
||||
# Upload via rsync with delta transfer and compression.
|
||||
# After the first deploy, only changed bytes are transferred.
|
||||
rsync -z -t --partial --progress \
|
||||
"$BUILT_BINARY" \
|
||||
"$HOST:$REMOTE_ABS_DIR/$BINARY_NAME"
|
||||
|
||||
# Set executable permissions (done separately for openrsync compatibility on macOS)
|
||||
ssh "$HOST" "chmod 755 $REMOTE_ABS_DIR/$BINARY_NAME"
|
||||
|
||||
echo ""
|
||||
echo "==> Done! Binary deployed to $HOST:$REMOTE_ABS_DIR/$BINARY_NAME"
|
||||
echo " (resolved from ~/$REMOTE_DIR/$BINARY_NAME on remote)"
|
||||
@@ -0,0 +1,68 @@
|
||||
'''
|
||||
Generates the `ExternalFontFamily` definitions used in `app/src/font_fallback.rs`.
|
||||
These definitions contain the URLs to each external fallback font we use in Warp.
|
||||
Generated code is sent to stdout.
|
||||
|
||||
This script will read our cloud storage bucket to retrieve the names of the fonts
|
||||
we support, and generate the code required to initialize static references for
|
||||
each font family.
|
||||
|
||||
Assumes that the fallback fonts in the prod `warp-static-assets` bucket are
|
||||
identical to the ones stored in the staging `warp-server-staging-static-assets`
|
||||
bucket.
|
||||
|
||||
Usage:
|
||||
1. Make sure the gcloud CLI is installed and you are authed via `gcloud auth login`.
|
||||
2. Run `python3 generate_families.py`
|
||||
3. Manually inspect the name for each font. The script will generate the name in
|
||||
title-case, but this isn't correct for some fonts (e.g. Noto Sans SC).
|
||||
'''
|
||||
|
||||
import subprocess
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def list_fonts():
|
||||
command = "gcloud storage ls --recursive 'gs://warp-static-assets/fallback-fonts/**.ttf'"
|
||||
return subprocess.check_output(command, shell=True, text=True).splitlines()
|
||||
|
||||
|
||||
def generate_families(font_uris):
|
||||
family_map = defaultdict(list)
|
||||
for uri in font_uris:
|
||||
parts = uri.removeprefix("gs://warp-static-assets/fallback-fonts/").split('/')
|
||||
family_name = parts[0]
|
||||
font_name = parts[1]
|
||||
family_map[family_name].append(font_name)
|
||||
|
||||
for family_name, font_names in family_map.items():
|
||||
print_family(family_name, font_names)
|
||||
|
||||
|
||||
def indent_level(level, s):
|
||||
indent = " " * level
|
||||
return indent + s
|
||||
|
||||
|
||||
def print_family(family_name, font_names):
|
||||
variable_name = family_name.replace('-', '_').upper()
|
||||
title_case_name = family_name.replace('-', ' ').title()
|
||||
|
||||
print(f"static ref {variable_name}: ExternalFontFamily = ExternalFontFamily {{")
|
||||
# Title-case is not correct for some fonts, e.g. "Noto Sans SC", so we add
|
||||
# a todo to make any manual adjustments.
|
||||
print(indent_level(1, f"name: \"{title_case_name}\", // TODO: double-check the title is correct"))
|
||||
print(indent_level(1, "font_urls: Arc::new(vec!["))
|
||||
for font_name in font_names:
|
||||
print(indent_level(2, f"url_for_font(\"{family_name}\", \"{font_name}\"),"))
|
||||
print(indent_level(1, "]),"))
|
||||
print("};")
|
||||
|
||||
|
||||
def main():
|
||||
font_uris = list_fonts()
|
||||
generate_families(font_uris)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,255 @@
|
||||
'''
|
||||
Generates the match statement used in `app/src/font_fallback.rs` to map Unicode
|
||||
code points to fallback fonts. Should be used in tandem with `generate-families.py`.
|
||||
Generated code is sent to stdout.
|
||||
|
||||
This script will read our cloud storage bucket to download the font data for each
|
||||
fallback font that we support. A directory "downloaded_fonts" will be created in
|
||||
the directory where the script is executed to contain the downloaded fonts. If
|
||||
that directory already exists, it is assumed that the fonts have previously been
|
||||
downloaded and skips downloading them again.
|
||||
|
||||
Assumptions:
|
||||
- The fallback fonts in the prod `warp-static-assets` bucket are identical to the
|
||||
ones stored in the staging `warp-server-staging-static-assets` bucket.
|
||||
- For each font family in the bucket, there is a variant that contains "Regular"
|
||||
in the filename.
|
||||
|
||||
Usage:
|
||||
1. Install the dependencies in `requirements.txt`.
|
||||
2. Make sure the gcloud CLI is installed and you are authed via `gcloud auth login`.
|
||||
3. Make sure you're running the script from `scripts/font_fallback`.
|
||||
4. Run `python3 generate-mappings.py`.
|
||||
'''
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from operator import itemgetter
|
||||
from fontTools.ttLib import TTFont
|
||||
from collections import defaultdict
|
||||
|
||||
# Represents priority for each font, with a lower value being higher priority.
|
||||
# Since a code point can be supported by multiple fonts, the script will map the
|
||||
# code point to the font with more priority.
|
||||
GLOBAL_ORDER = {
|
||||
"Hack Nerd Font": 1,
|
||||
"Noto Color Emoji": 2,
|
||||
"Noto Sans Symbols": 3,
|
||||
"Noto Sans Symbols 2": 4,
|
||||
"Noto Sans SC": 5,
|
||||
"Noto Sans JP": 6,
|
||||
"Noto Sans Devanagari": 7,
|
||||
}
|
||||
|
||||
# By default, we try to coalesce code point ranges. e.g. If we have the
|
||||
# following mappings:
|
||||
# U+10000..U+10002 -> Noto Sans SC
|
||||
# U+10004..U+10005 -> Noto Sans SC
|
||||
# If U+10003 is not represented by any font, we will merge these code points into
|
||||
# a single range even though there is an unsupported code point in the range:
|
||||
# U+10000..U+10005 -> Noto Sans SC
|
||||
# This is done to reduce the total number of mappings and is useful for handling
|
||||
# Unicode blocks with rare characters where font support for these characters is
|
||||
# sparse.
|
||||
#
|
||||
# However, we don't want to do this for some fonts where unsupported code points
|
||||
# could represent icons or private use areas in Unicode. Those fonts are added
|
||||
# to this set.
|
||||
FONTS_NOT_TO_COALESCE = set([
|
||||
"Noto Sans Symbols",
|
||||
"Noto Sans Symbols 2",
|
||||
"Noto Color Emoji",
|
||||
"Hack Nerd Font"
|
||||
])
|
||||
|
||||
HACK_FONT_FILEPATH = "../../app/assets/bundled/fonts/hack/Hack-Regular.ttf"
|
||||
ROBOTO_FONT_FILEPATH = "../../app/assets/bundled/fonts/roboto/Roboto-Regular.ttf"
|
||||
FONT_DOWNLOAD_DIR = "./downloaded_fonts"
|
||||
|
||||
|
||||
def download_fallback_fonts():
|
||||
if os.path.exists(FONT_DOWNLOAD_DIR):
|
||||
# Fonts already exist, no need to download
|
||||
return
|
||||
|
||||
os.mkdir(FONT_DOWNLOAD_DIR)
|
||||
command = f"gcloud storage cp 'gs://warp-static-assets/fallback-fonts/**/*Regular*.ttf' '{FONT_DOWNLOAD_DIR}'"
|
||||
return_code = subprocess.call(command, shell=True)
|
||||
if return_code != 0:
|
||||
sys.exit("Failed to download fonts from GCP")
|
||||
|
||||
|
||||
def get_global_order(font_name):
|
||||
if font_name in GLOBAL_ORDER:
|
||||
return GLOBAL_ORDER[font_name]
|
||||
else:
|
||||
return len(GLOBAL_ORDER) + 1
|
||||
|
||||
|
||||
def get_default_fonts():
|
||||
return [TTFont(HACK_FONT_FILEPATH), TTFont(ROBOTO_FONT_FILEPATH)]
|
||||
|
||||
|
||||
# Returns a `TTFont` object for each fallback font, sorted by their global order.
|
||||
def get_fallback_fonts(fallback_fonts_dir):
|
||||
fonts = []
|
||||
for file in os.listdir(fallback_fonts_dir):
|
||||
if file.endswith(".ttf"):
|
||||
path = os.path.join(fallback_fonts_dir, file)
|
||||
font = TTFont(path)
|
||||
|
||||
font_name = get_font_name(font)
|
||||
global_order = get_global_order(font_name)
|
||||
fonts.append((font, global_order))
|
||||
|
||||
fonts.sort(key=itemgetter(1))
|
||||
return [font for (font, _) in fonts]
|
||||
|
||||
|
||||
def get_font_name(font):
|
||||
return font['name'].getBestFamilyName()
|
||||
|
||||
|
||||
def supported_code_points(font):
|
||||
code_points = set()
|
||||
for table in font['cmap'].tables:
|
||||
if table.isUnicode():
|
||||
code_points.update(table.cmap.keys())
|
||||
return code_points
|
||||
|
||||
|
||||
def common_code_points(fonts):
|
||||
code_points = [supported_code_points(font) for font in fonts]
|
||||
return set.intersection(*code_points)
|
||||
|
||||
|
||||
def generate_mapping(default_fonts, fallback_fonts):
|
||||
default_code_points = common_code_points(default_fonts)
|
||||
mapping = {}
|
||||
for font in reversed(fallback_fonts):
|
||||
font_name = get_font_name(font)
|
||||
font_code_points = supported_code_points(font)
|
||||
for code_point in font_code_points:
|
||||
if code_point in default_code_points:
|
||||
continue
|
||||
mapping[code_point] = font_name
|
||||
ranges = coalesce_ranges(collapse_to_ranges(mapping))
|
||||
font_ranges_map = collect_ranges_to_map(ranges)
|
||||
print_match_statement(font_ranges_map)
|
||||
|
||||
|
||||
# Takes a mapping of individual code points -> fallback fonts and merges them
|
||||
# into ranges where consecutive code points map to the same fallback font.
|
||||
def collapse_to_ranges(mapping):
|
||||
mapping_list = [(k, v) for k, v in mapping.items()]
|
||||
mapping_list.sort(key=itemgetter(0))
|
||||
|
||||
ranges = []
|
||||
prev_font = None
|
||||
active_range = None
|
||||
|
||||
for code_point, font in mapping_list:
|
||||
if active_range and prev_font and (active_range[1], prev_font) != (code_point - 1, font):
|
||||
ranges.append((active_range, prev_font))
|
||||
active_range = None
|
||||
|
||||
if not active_range:
|
||||
active_range = (code_point, code_point)
|
||||
else:
|
||||
active_range = (active_range[0], code_point)
|
||||
prev_font = font
|
||||
|
||||
if active_range and prev_font:
|
||||
ranges.append((active_range, prev_font))
|
||||
|
||||
return ranges
|
||||
|
||||
|
||||
# Takes a mapping of code point ranges -> fallback fonts and coalesces them. See
|
||||
# the comment on `FONTS_NOT_TO_COALESCE` for more details on coalescing.
|
||||
def coalesce_ranges(ranges):
|
||||
new_ranges = []
|
||||
prev_font = None
|
||||
active_range = None
|
||||
|
||||
for cur_range, font in ranges:
|
||||
if font in FONTS_NOT_TO_COALESCE:
|
||||
if active_range and prev_font:
|
||||
new_ranges.append((active_range, prev_font))
|
||||
new_ranges.append((cur_range, font))
|
||||
prev_font = None
|
||||
active_range = None
|
||||
continue
|
||||
|
||||
if active_range and prev_font and prev_font != font:
|
||||
new_ranges.append((active_range, prev_font))
|
||||
active_range = None
|
||||
|
||||
if not active_range:
|
||||
active_range = cur_range
|
||||
else:
|
||||
active_range = (active_range[0], cur_range[1])
|
||||
prev_font = font
|
||||
|
||||
if active_range and prev_font:
|
||||
ranges.append((active_range, prev_font))
|
||||
|
||||
return new_ranges
|
||||
|
||||
|
||||
# Collects the mappings into a dictionary where each font is mapped to a list of
|
||||
# all the code point ranges that it supports.
|
||||
def collect_ranges_to_map(ranges):
|
||||
font_ranges_map = defaultdict(list)
|
||||
for cur_range, font in ranges:
|
||||
font_ranges_map[font].append(cur_range)
|
||||
return font_ranges_map
|
||||
|
||||
|
||||
def match_arm(code_point_range, font_name):
|
||||
range_start, range_end = code_point_range
|
||||
constant_case_font_name = font_name.replace(" ", "_").upper()
|
||||
|
||||
match_start = f"\\u{{{range_start:04X}}}"
|
||||
match_end = f"\\u{{{range_end:04X}}}"
|
||||
font_family = f"Some({constant_case_font_name}.clone())"
|
||||
return f"'{match_start}'..='{match_end}' => {font_family},"
|
||||
|
||||
|
||||
def print_font_ranges(font_name, ranges):
|
||||
constant_case_font_name = font_name.replace(" ", "_").upper()
|
||||
font_family = f"Some({constant_case_font_name}.clone())"
|
||||
for i, (range_start, range_end) in enumerate(ranges):
|
||||
match_start = f"\\u{{{range_start:04X}}}"
|
||||
match_end = f"\\u{{{range_end:04X}}}"
|
||||
line = ""
|
||||
if i > 0:
|
||||
line += "| "
|
||||
line += f"'{match_start}'..='{match_end}'"
|
||||
if i == len(ranges) - 1:
|
||||
line += f" => {font_family},"
|
||||
print(line)
|
||||
|
||||
|
||||
def print_match_statement(font_ranges_map):
|
||||
print("match ch {")
|
||||
|
||||
for font_name, ranges in font_ranges_map.items():
|
||||
print_font_ranges(font_name, ranges)
|
||||
|
||||
print("_ => None")
|
||||
print("}")
|
||||
|
||||
|
||||
def main():
|
||||
default_fonts = get_default_fonts()
|
||||
|
||||
download_fallback_fonts()
|
||||
fallback_fonts = get_fallback_fonts(FONT_DOWNLOAD_DIR)
|
||||
|
||||
generate_mapping(default_fonts, fallback_fonts)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
fonttools==4.61.0
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs cargo-binstall if not present, or updates it if already installed.
|
||||
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
# Install cargo-binstall, then/or use it to update itself.
|
||||
if ! command -v cargo-binstall; then
|
||||
cargo install cargo-binstall@1.14.3 --locked
|
||||
fi
|
||||
|
||||
cargo binstall cargo-binstall
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs all cargo-managed dependencies required to build Warp.
|
||||
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
"$PWD"/script/install_cargo_binstall
|
||||
|
||||
# Use cargo-binstall to install the Diesel CLI.
|
||||
#
|
||||
# This is only needed for local development, so we skip it when running on GitHub
|
||||
# (either for CI or building a release).
|
||||
if [ "${GITHUB_ACTIONS}" != "true" ]; then
|
||||
cargo binstall --force -y diesel_cli
|
||||
fi
|
||||
|
||||
# Install the internal channel config binary. This will fail gracefully for
|
||||
# external contributors who don't have access to the private configuration.
|
||||
"$PWD"/script/install_channel_config || echo "Skipping internal channel config installation (no repo access)."
|
||||
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
"$PWD"/script/macos/install_build_deps
|
||||
fi
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to install cargo-bundle for CI. This is used as an optimization to speed up CI instead of having to install and
|
||||
# compile cargo-bundle on each CI workflow run.
|
||||
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
cargo_location=$(dirname "$(which cargo)")
|
||||
if test -f "$cargo_location/cargo-bundle"; then
|
||||
echo "cargo-bundle already exists, not installing."
|
||||
else
|
||||
set -e
|
||||
curl https://storage.googleapis.com/cached_crates/cargo-bundle.zip --output cargo-bundle.zip
|
||||
unzip -o cargo-bundle.zip
|
||||
chmod 755 cargo-bundle
|
||||
cp cargo-bundle "$cargo_location"
|
||||
echo "Successfully moved cached cargo-bundle to location $cargo_location"
|
||||
fi
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs all cargo-managed dependencies required to build Warp release
|
||||
# artifacts (e.g., bundled license generation).
|
||||
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
NO_BUILD_DEPS=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--no-build-deps)
|
||||
NO_BUILD_DEPS=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$NO_BUILD_DEPS" = false ]; then
|
||||
"$PWD"/script/install_cargo_build_deps
|
||||
fi
|
||||
|
||||
# Install cargo-about for generating third-party license attribution.
|
||||
cargo install --locked cargo-about@0.8.4
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs all cargo-managed dependencies required to build and test Warp.
|
||||
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
"$PWD"/script/install_cargo_build_deps
|
||||
|
||||
# note: keep this version in sync with wgslfmt in .github/workflows/ci.yml
|
||||
cargo install --git https://github.com/wgsl-analyzer/wgsl-analyzer --tag "2025-06-28" wgslfmt
|
||||
|
||||
"$PWD"/script/install_cargo_binstall
|
||||
|
||||
# Install nextest, which we use as our test execution harness.
|
||||
cargo binstall --secure --no-confirm --no-discover-github-token cargo-nextest
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs the warp_channel_config binary at a pinned revision.
|
||||
#
|
||||
# This script acts as a lockfile: the REVISION variable pins the exact version
|
||||
# of the config binary to the code that reads its output. Update REVISION when
|
||||
# the config binary changes.
|
||||
#
|
||||
# Usage:
|
||||
# ./script/install_channel_config # install if not already at pinned rev
|
||||
# ./script/install_channel_config --force # force reinstall
|
||||
|
||||
REPO="ssh://git@github.com/warpdotdev/warp-channel-config.git"
|
||||
REVISION="97a199b5bba11af9c7d3bd43dfb3669316d1f0f8"
|
||||
BIN_NAME="warp-channel-config"
|
||||
|
||||
# Check for repo access before attempting to `cargo install` the binary.
|
||||
if ! git ls-remote --exit-code "${REPO}" HEAD >/dev/null 2>&1; then
|
||||
echo "Cannot access ${REPO} (no SSH access?). Skipping install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FORCE_FLAG=""
|
||||
if [ "${1:-}" = "--force" ]; then
|
||||
FORCE_FLAG="--force"
|
||||
fi
|
||||
|
||||
cargo install ${FORCE_FLAG} --git "${REPO}" --rev "${REVISION}" --bin "${BIN_NAME}"
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Script to install Rust for CI.
|
||||
#
|
||||
# This script should be platform-agnostic (eg. no unix-only references like /dev/null).
|
||||
|
||||
set -e
|
||||
|
||||
# Define some control sequences for text formatting.
|
||||
red="\033[0;31m"
|
||||
reset="\033[0m"
|
||||
|
||||
CI_FLAGS=""
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
CI_FLAGS="-s -- -y"
|
||||
fi
|
||||
|
||||
# Install Rust.
|
||||
if ! command -v cargo; then
|
||||
echo "⬇️ Installing rust..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh $CI_FLAGS
|
||||
CARGO_ENV_FILE="$HOME/.cargo/env"
|
||||
if [[ -f "$CARGO_ENV_FILE" ]]; then
|
||||
source "$CARGO_ENV_FILE"
|
||||
else
|
||||
echo -e "⚠️ ${red}Please start a new terminal session so that cargo is in your PATH.${reset}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env -S pwsh -NoProfile
|
||||
|
||||
param(
|
||||
[switch]$ci,
|
||||
[switch]$fix
|
||||
)
|
||||
|
||||
$rulesFile = './.PSScriptAnalyzerSettings.psd1'
|
||||
$sources = @(
|
||||
'./app/assets/bundled/bootstrap/',
|
||||
'./script/windows/'
|
||||
)
|
||||
|
||||
foreach ($source in $sources) {
|
||||
Write-Output "Inspecting scripts in '$source'"
|
||||
$scriptAnalyzerParams = @{
|
||||
Settings = $rulesFile
|
||||
Path = $source
|
||||
ReportSummary = $true
|
||||
Recurse = $true
|
||||
Fix = $fix
|
||||
}
|
||||
$results = Invoke-ScriptAnalyzer @scriptAnalyzerParams
|
||||
Write-Output $results
|
||||
$errorsAndWarnings = $results | Where-Object { $_.Severity -ge [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticSeverity]::Warning }
|
||||
$problemCount = ($errorsAndWarnings | Measure-Object).Count
|
||||
if ($problemCount -gt 0 -and $ci) {
|
||||
throw "Lint failed with $problemCount problems"
|
||||
}
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Update the apt cache before installing dependencies. It can be slow, so the install scripts
|
||||
# don't do it (to avoid doing it more than once).
|
||||
sudo apt update -y
|
||||
|
||||
# Install all dependencies needed to build, run, and test Warp.
|
||||
"$PWD"/script/linux/install_test_deps
|
||||
|
||||
# Install linuxdeploy.
|
||||
"$PWD"/script/linux/install_linuxdeploy
|
||||
|
||||
# Make sure we're authenticated with the gcloud CLI, otherwise SSH integration
|
||||
# tests won't work.
|
||||
if [[ -z "$(gcloud auth print-identity-token)" ]]; then
|
||||
echo "gcloud CLI authentication missing. Press enter to continue..."
|
||||
read var
|
||||
gcloud auth login
|
||||
fi
|
||||
|
||||
echo "✅ Your machine is bootstrapped and ready to go. :)"
|
||||
Executable
+277
@@ -0,0 +1,277 @@
|
||||
#!/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).
|
||||
CARGO_PROFILE="release-lto-debug_assertions"
|
||||
else
|
||||
CARGO_PROFILE="release-lto"
|
||||
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"
|
||||
fi
|
||||
|
||||
# Artifact-specific binary naming
|
||||
if [[ "$ARTIFACT" == "cli" ]]; then
|
||||
# For CLI artifacts, use oz instead of warp as the binary name
|
||||
BINARY_NAME="${BINARY_NAME/warp/oz}"
|
||||
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"
|
||||
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
|
||||
|
||||
# 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
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds an AppImage as part of the bundling process.
|
||||
#
|
||||
# Required environment variables:
|
||||
# - WORKSPACE_ROOT_DIR: The root directory of the Cargo workspace.
|
||||
# - DIST_DIR: A temporary directory we can use for staging files as we build the package.
|
||||
# - OUT_DIR: The directory into which we should place the final package.
|
||||
# - CARGO_TARGET_OUTPUT_DIR: The cargo target directory containing build output.
|
||||
# - RELEASE_CHANNEL: The release channel we're bundling (e.g.: "dev").
|
||||
# - BUNDLE_ID: The app ID for the bundle (e.g.: "dev.warp.WarpDev").
|
||||
# - EXECUTABLE_PATH: The path to the compiled executable we want to install.
|
||||
# - BINARY_NAME: The name that the compiled executable should have on the target machine.
|
||||
# - APPIMAGE_NAME: The name of the AppImage file to produce.
|
||||
|
||||
STAGE_DIR="$DIST_DIR/appimagepkg"
|
||||
APP_DIR="$DIST_DIR/AppDir"
|
||||
|
||||
# Extract channel suffix from BINARY_NAME
|
||||
if [ "$ARTIFACT" = "cli" ]; then
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#oz}"
|
||||
else
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#warp}"
|
||||
fi
|
||||
PACKAGE_NAME="warp-terminal$CHANNEL_SUFFIX"
|
||||
|
||||
|
||||
# Adding NO_STRIP to stop linuxdeploy from attempting to strip symbols
|
||||
# from binaries built on a different arch than the current symbol.
|
||||
BUILD_ARCH="${BUILD_ARCH:-$(uname -m)}"
|
||||
if [ "$BUILD_ARCH" != "$(uname -m)" ]; then
|
||||
export NO_STRIP=1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
# Delete the directories used as staging areas during the bundling
|
||||
# process.
|
||||
if [ -d "$STAGE_DIR" ]; then
|
||||
rm -rf "$STAGE_DIR"
|
||||
fi
|
||||
if [ -d "$APP_DIR" ]; then
|
||||
rm -rf "$APP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run cleanup() when the script terminates (whether it succeeded or failed).
|
||||
trap cleanup EXIT
|
||||
|
||||
# Construct a directory to contain the staged package contents.
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR"
|
||||
|
||||
# Populate the directory with the package contents, in the appropriate
|
||||
# locations.
|
||||
OPT_DIR="/opt/warpdotdev/$PACKAGE_NAME"
|
||||
source "$WORKSPACE_ROOT_DIR/script/linux/bundle_install" "$STAGE_DIR"
|
||||
|
||||
# Modify the "Exec=" line in the .desktop file to use the name of the installed
|
||||
# binary and not our /usr/bin symlink (which doesn't exist in an AppImage).
|
||||
sed -i -E 's/Exec=warp-terminal/Exec=warp/' "$STAGE_DIR/usr/share/applications/$BUNDLE_ID.desktop"
|
||||
|
||||
# Find all icon files from inside the package staging directory.
|
||||
ICON_FILES=( $(find "$STAGE_DIR/usr/share/icons" -name "*.png") )
|
||||
|
||||
# Make sure there's no lingering AppDir from a previous execution.
|
||||
rm -rf "$APP_DIR"
|
||||
|
||||
# Run linuxdeploy to create an appropriately-structured AppDir and turn it into
|
||||
# an AppImage, located in OUT_DIR.
|
||||
#
|
||||
# We use a custom input plugin (bundled-resources) to copy Warp's bundled
|
||||
# resources into the AppDir alongside the executable, since linuxdeploy only
|
||||
# deploys the binary, desktop file, and icons by default.
|
||||
cd "$OUT_DIR"
|
||||
echo "Running linuxdeploy to create the AppImage"
|
||||
export WARP_BINARY_NAME="$BINARY_NAME"
|
||||
export WARP_PACKAGE_NAME="$PACKAGE_NAME"
|
||||
export WARP_BUNDLED_RESOURCES_DIR="${STAGE_DIR}${OPT_DIR}/resources"
|
||||
export PATH="$WORKSPACE_ROOT_DIR/script/linux:$PATH"
|
||||
linuxdeploy \
|
||||
--appdir "$APP_DIR" \
|
||||
--executable "${STAGE_DIR}${OPT_DIR}/$BINARY_NAME" \
|
||||
--desktop-file "$STAGE_DIR/usr/share/applications/$BUNDLE_ID.desktop" \
|
||||
$(for i in "${ICON_FILES[@]}"; do echo "--icon-file $i "; done) \
|
||||
--plugin warp \
|
||||
--output appimage
|
||||
|
||||
APPIMAGE_PATH="$OUT_DIR/$APPIMAGE_NAME"
|
||||
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# location of the AppImage so it can be referenced by subsequent actions.
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
echo "::echo::on"
|
||||
echo "appimage_path=$APPIMAGE_PATH" >> "$GITHUB_OUTPUT"
|
||||
echo "::echo::off"
|
||||
fi
|
||||
|
||||
echo "Successfully built AppImage at $APPIMAGE_PATH!"
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds an Arch Linux package as part of the bundling process.
|
||||
#
|
||||
# Required environment variables:
|
||||
# - WORKSPACE_ROOT_DIR: The root directory of the Cargo workspace.
|
||||
# - DIST_DIR: A temporary directory we can use for staging files as we build the package.
|
||||
# - OUT_DIR: The directory into which we should place the final package.
|
||||
# - CARGO_TARGET_OUTPUT_DIR: The cargo target directory containing build output.
|
||||
# - RELEASE_CHANNEL: The release channel we're bundling (e.g.: "dev").
|
||||
# - BUNDLE_ID: The app ID for the bundle (e.g.: "dev.warp.WarpDev").
|
||||
# - EXECUTABLE_PATH: The path to the compiled executable we want to install.
|
||||
# - BINARY_NAME: The name that the compiled executable should have on the target machine.
|
||||
# - ARTIFACT: Which artifact to build: app or cli.
|
||||
|
||||
set -e
|
||||
|
||||
# Extract channel suffix from BINARY_NAME
|
||||
if [ "$ARTIFACT" = "cli" ]; then
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#oz}"
|
||||
else
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#warp}"
|
||||
fi
|
||||
|
||||
# On Linux, we keep the `-stable` suffix for stable to avoid package conflicts.
|
||||
# The binary name is still `oz`, and the repo name is still `warpdotdev`.
|
||||
if [ "$ARTIFACT" = "cli" -a "$RELEASE_CHANNEL" = "stable" ]; then
|
||||
CHANNEL_SUFFIX="-stable"
|
||||
fi
|
||||
|
||||
if [ "$ARTIFACT" = "app" ]; then
|
||||
PACKAGE_NAME="warp-terminal$CHANNEL_SUFFIX"
|
||||
elif [ "$ARTIFACT" = "cli" ]; then
|
||||
PACKAGE_NAME="oz$CHANNEL_SUFFIX"
|
||||
else
|
||||
echo "::error ::Unknown ARTIFACT: $ARTIFACT (expected 'app' or 'cli')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_ARCH="${BUILD_ARCH:-$(uname -m)}"
|
||||
if [ "$BUILD_ARCH" = "aarch64" ]; then
|
||||
ARCH="aarch64"
|
||||
else
|
||||
ARCH="x86_64"
|
||||
fi
|
||||
|
||||
PKGDIR="$DIST_DIR/archpkg"
|
||||
|
||||
cleanup() {
|
||||
# Delete the AppDir that was used as a staging area during the bundling
|
||||
# process.
|
||||
if [ -d "$PKGDIR" ]; then
|
||||
rm -rf "$PKGDIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run cleanup() when the script terminates (whether it succeeded or failed).
|
||||
trap cleanup EXIT
|
||||
|
||||
# Construct a directory that contains all of the output data.
|
||||
rm -rf "$PKGDIR"
|
||||
mkdir -p "$PKGDIR"
|
||||
|
||||
# Bundle the base filesystem into an archive to be unpacked by makepkg.
|
||||
mkdir "$PKGDIR/stage"
|
||||
OPT_DIR="/opt/warpdotdev/$PACKAGE_NAME"
|
||||
source "$WORKSPACE_ROOT_DIR/script/linux/bundle_install" "$PKGDIR/stage"
|
||||
tar cvf "$PKGDIR/data.tar" -C "$PKGDIR/stage" .
|
||||
|
||||
# Move the Arch-specific package metadata into the package directory.
|
||||
VERSION="$GIT_RELEASE_TAG"
|
||||
RELEASE=1
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; s#@@VERSION@@#$VERSION#g; s#@@RELEASE@@#$RELEASE#g; s#@@ARCH@@#$ARCH#g; s#@@OUTDIR@@#$OUT_DIR#g; s#@@BINARY_NAME@@#$BINARY_NAME#g" \
|
||||
< "$WORKSPACE_ROOT_DIR/resources/linux/arch/$ARTIFACT/PKGBUILD.template" \
|
||||
> "$PKGDIR/PKGBUILD"
|
||||
|
||||
# Only create wrapper script for app artifact
|
||||
if [ "$ARTIFACT" = "app" ]; then
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; s#@@BINARY_NAME@@#$BINARY_NAME#g" \
|
||||
< "$WORKSPACE_ROOT_DIR/resources/linux/arch/$ARTIFACT/warp.sh.template" \
|
||||
> "$PKGDIR/$PACKAGE_NAME.sh"
|
||||
fi
|
||||
|
||||
# Build the actual package.
|
||||
#
|
||||
# We install any needed build and runtime dependencies before making the
|
||||
# package, and remove them again afterwards. We also skip verifying
|
||||
# checksums for the source files, as we generate them above and there's not
|
||||
# much reason to generate the checksums and then verify them immediately
|
||||
# afterwards.
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
# If we're running in a GitHub action, don't ask for confirmation before
|
||||
# installing packages.
|
||||
EXTRA_ARGS="--noconfirm"
|
||||
fi
|
||||
cd "$PKGDIR"
|
||||
PKGDEST="$OUT_DIR" CARCH="$ARCH" makepkg -cfrs --needed --skipchecksums "$EXTRA_ARGS"
|
||||
|
||||
PACKAGE_PATH="$OUT_DIR/${PACKAGE_NAME}-${VERSION}-${RELEASE}-${ARCH}.pkg.tar.zst"
|
||||
|
||||
echo "Successfully built Arch package at $PACKAGE_PATH!"
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds a .deb package as part of the bundling process.
|
||||
#
|
||||
# Required environment variables:
|
||||
# - WORKSPACE_ROOT_DIR: The root directory of the Cargo workspace.
|
||||
# - DIST_DIR: A temporary directory we can use for staging files as we build the package.
|
||||
# - OUT_DIR: The directory into which we should place the final package.
|
||||
# - CARGO_TARGET_OUTPUT_DIR: The cargo target directory containing build output.
|
||||
# - RELEASE_CHANNEL: The release channel we're bundling (e.g.: "dev").
|
||||
# - BUNDLE_ID: The app ID for the bundle (e.g.: "dev.warp.WarpDev").
|
||||
# - EXECUTABLE_PATH: The path to the compiled executable we want to install.
|
||||
# - BINARY_NAME: The name that the compiled executable should have on the target machine.
|
||||
# - ARTIFACT: Which artifact to build: app or cli.
|
||||
|
||||
set -e
|
||||
|
||||
# Extract channel suffix from BINARY_NAME
|
||||
if [ "$ARTIFACT" = "cli" ]; then
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#oz}"
|
||||
else
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#warp}"
|
||||
fi
|
||||
REPO_NAME="warpdotdev$CHANNEL_SUFFIX"
|
||||
|
||||
# On Linux, we keep the `-stable` suffix for stable to avoid package conflicts.
|
||||
# The binary name is still `oz`, and the repo name is still `warpdotdev`.
|
||||
if [ "$ARTIFACT" = "cli" -a "$RELEASE_CHANNEL" = "stable" ]; then
|
||||
CHANNEL_SUFFIX="-stable"
|
||||
fi
|
||||
|
||||
case "$ARTIFACT" in
|
||||
app)
|
||||
PACKAGE_NAME="warp-terminal$CHANNEL_SUFFIX"
|
||||
;;
|
||||
cli)
|
||||
PACKAGE_NAME="oz$CHANNEL_SUFFIX"
|
||||
;;
|
||||
*)
|
||||
echo "::error ::Unknown ARTIFACT: $ARTIFACT (expected 'app' or 'cli')"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
TEMPLATE_DIR="$WORKSPACE_ROOT_DIR/resources/linux/debian/$ARTIFACT"
|
||||
|
||||
# Add a simple test to make sure we're generating the appropriate repository
|
||||
# name for the stable channel.
|
||||
if [ "$RELEASE_CHANNEL" = "stable" ]; then
|
||||
if [ "$REPO_NAME" != "warpdotdev" ]; then
|
||||
echo "::error ::Unexpected repo name for $RELEASE_CHANNEL channel: $REPO_NAME (expected \"warpdotdev\")"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
BUILD_ARCH="${BUILD_ARCH:-$(uname -m)}"
|
||||
if [ "$BUILD_ARCH" = "aarch64" ]; then
|
||||
ARCH="arm64"
|
||||
else
|
||||
ARCH="amd64"
|
||||
fi
|
||||
|
||||
FULL_PACKAGE_NAME="$PACKAGE_NAME-$ARCH"
|
||||
PKGDIR="$DIST_DIR/debpkg/$FULL_PACKAGE_NAME"
|
||||
|
||||
cleanup() {
|
||||
# Delete the AppDir that was used as a staging area during the bundling
|
||||
# process.
|
||||
if [ -d "$PKGDIR" ]; then
|
||||
rm -rf "$PKGDIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run cleanup() when the script terminates (whether it succeeded or failed).
|
||||
trap cleanup EXIT
|
||||
|
||||
# Construct a directory that contains all of the output data.
|
||||
rm -rf "$PKGDIR"
|
||||
mkdir -p "$PKGDIR"
|
||||
|
||||
# Populate the directory with the package contents, in the appropriate
|
||||
# locations.
|
||||
OPT_DIR="/opt/warpdotdev/$PACKAGE_NAME"
|
||||
source "$WORKSPACE_ROOT_DIR/script/linux/bundle_install" "$PKGDIR"
|
||||
|
||||
# Move the Debian-specific package metadata into the package directory.
|
||||
DEBIAN_DIR="$PKGDIR/DEBIAN"
|
||||
VERSION="$(echo "$GIT_RELEASE_TAG" | sed -E "s/^v//; s/([a-z]+)_/\1./")"
|
||||
mkdir "$DEBIAN_DIR"
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; s#@@VERSION@@#$VERSION#g; s#@@ARCH@@#$ARCH#g" \
|
||||
< "$TEMPLATE_DIR/control.template" \
|
||||
> "$DEBIAN_DIR/control"
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; s#@@BINARY_NAME@@#$BINARY_NAME#g; s#@@OPTDIR@@#$OPT_DIR#g; s#@@REPO_NAME@@#$REPO_NAME#g; s#@@CHANNEL@@#$RELEASE_CHANNEL#g; s#@@ARCH@@#$ARCH#g" \
|
||||
< "$TEMPLATE_DIR/postinst.template" \
|
||||
> "$DEBIAN_DIR/postinst"
|
||||
sed \
|
||||
"s#@@REPO_NAME@@#$REPO_NAME#g; s#@@CHANNEL@@#$RELEASE_CHANNEL#g; s#@@ARCH@@#$ARCH#g" \
|
||||
< "$WORKSPACE_ROOT_DIR/resources/linux/debian/common/postinst.repo.template" \
|
||||
>> "$DEBIAN_DIR/postinst"
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; s#@@OPTDIR@@#$OPT_DIR#g; s#@@REPO_NAME@@#$REPO_NAME#g" \
|
||||
< "$TEMPLATE_DIR/postrm.template" \
|
||||
> "$DEBIAN_DIR/postrm"
|
||||
sed \
|
||||
"s#@@REPO_NAME@@#$REPO_NAME#g" \
|
||||
< "$WORKSPACE_ROOT_DIR/resources/linux/debian/common/postrm.repo.template" \
|
||||
>> "$DEBIAN_DIR/postrm"
|
||||
|
||||
# Make the package scripts executable.
|
||||
chmod 755 \
|
||||
"$DEBIAN_DIR/postinst" \
|
||||
"$DEBIAN_DIR/postrm"
|
||||
|
||||
# Build the actual package.
|
||||
cd "$DIST_DIR/debpkg"
|
||||
fakeroot dpkg-deb -b "$PACKAGE_NAME-$ARCH" "$OUT_DIR"
|
||||
|
||||
echo "Successfully built .deb package at $OUT_DIR/${PACKAGE_NAME}_${VERSION}_${ARCH}.deb!"
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Installs everything built by the bundle script into the given directory.
|
||||
#
|
||||
# This is intended to be used when building Linux packages (e.g.: .deb).
|
||||
#
|
||||
# Required environment variables:
|
||||
# - WORKSPACE_ROOT_DIR: The root directory of the Cargo workspace.
|
||||
# - CARGO_TARGET_OUTPUT_DIR: The cargo target directory containing build output.
|
||||
# - OPT_DIR: The absolute path to our install directory (under /opt) on the target machine.
|
||||
# - RELEASE_CHANNEL: The release channel we're bundling (e.g.: "dev").
|
||||
# - BUNDLE_ID: The app ID for the bundle (e.g.: "dev.warp.WarpDev").
|
||||
# - EXECUTABLE_PATH: The path to the compiled executable we want to install.
|
||||
# - BINARY_NAME: The name that the compiled executable should have on the target machine.
|
||||
# - ARTIFACT: Which artifact to build: app or cli.
|
||||
|
||||
set -e
|
||||
|
||||
# The target directory should be the first (and only) positional argument.
|
||||
if [[ $# -gt 1 ]]; then
|
||||
echo "Expected 1 argument but received $#!"
|
||||
exit 1
|
||||
fi
|
||||
TARGET_DIR="$1"
|
||||
|
||||
# Make sure the target directory is empty.
|
||||
if [[ ! -z "$(ls -A "$TARGET_DIR")" ]]; then
|
||||
echo "Target directory '$TARGET_DIR' not empty!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy files to the desired output locations within the target dir, stripping
|
||||
# debugging symbols from the installed binaries.
|
||||
install -D "$EXECUTABLE_PATH" "${TARGET_DIR}${OPT_DIR}/$BINARY_NAME"
|
||||
# Only install desktop file and icons if the artifact is "app"
|
||||
if [[ "$ARTIFACT" == "app" ]]; then
|
||||
install -Dm644 "$WORKSPACE_ROOT_DIR/app/channels/$RELEASE_CHANNEL/$BUNDLE_ID.desktop" "${TARGET_DIR}/usr/share/applications/$BUNDLE_ID.desktop"
|
||||
for size in 16x16 32x32 64x64 128x128 256x256 512x512; do
|
||||
src_path="$WORKSPACE_ROOT_DIR/app/channels/$RELEASE_CHANNEL/icon/no-padding/$size.png"
|
||||
if [[ -f "$src_path" ]]; then
|
||||
install -Dm644 "$src_path" "${TARGET_DIR}/usr/share/icons/hicolor/$size/apps/$BUNDLE_ID.png"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Prepare the bundled resources directory.
|
||||
"$WORKSPACE_ROOT_DIR/script/prepare_bundled_resources" "${TARGET_DIR}/${OPT_DIR}/resources" "$RELEASE_CHANNEL" "$CARGO_PROFILE"
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds a .rpm package as part of the bundling process.
|
||||
#
|
||||
# Required environment variables:
|
||||
# - WORKSPACE_ROOT_DIR: The root directory of the Cargo workspace.
|
||||
# - DIST_DIR: A temporary directory we can use for staging files as we build the package.
|
||||
# - OUT_DIR: The directory into which we should place the final package.
|
||||
# - CARGO_TARGET_OUTPUT_DIR: The cargo target directory containing build output.
|
||||
# - RELEASE_CHANNEL: The release channel we're bundling (e.g.: "dev").
|
||||
# - BUNDLE_ID: The app ID for the bundle (e.g.: "dev.warp.WarpDev").
|
||||
# - EXECUTABLE_PATH: The path to the compiled executable we want to install.
|
||||
# - BINARY_NAME: The name that the compiled executable should have on the target machine.
|
||||
# - ARTIFACT: Which artifact to build: app or cli.
|
||||
|
||||
set -e
|
||||
|
||||
# Extract channel suffix from BINARY_NAME
|
||||
if [ "$ARTIFACT" = "cli" ]; then
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#oz}"
|
||||
else
|
||||
CHANNEL_SUFFIX="${BINARY_NAME#warp}"
|
||||
fi
|
||||
REPO_NAME="warpdotdev$CHANNEL_SUFFIX"
|
||||
|
||||
# On Linux, we keep the `-stable` suffix for stable to avoid package conflicts.
|
||||
# The binary name is still `oz`, and the repo name is still `warpdotdev`.
|
||||
if [ "$ARTIFACT" = "cli" -a "$RELEASE_CHANNEL" = "stable" ]; then
|
||||
CHANNEL_SUFFIX="-stable"
|
||||
fi
|
||||
|
||||
ARTIFACT="${ARTIFACT:-app}"
|
||||
if [ "$ARTIFACT" = "app" ]; then
|
||||
PACKAGE_NAME="warp-terminal$CHANNEL_SUFFIX"
|
||||
elif [ "$ARTIFACT" = "cli" ]; then
|
||||
PACKAGE_NAME="oz$CHANNEL_SUFFIX"
|
||||
else
|
||||
echo "::error ::Unknown ARTIFACT: $ARTIFACT (expected 'app' or 'cli')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_ARCH="${BUILD_ARCH:-$(uname -m)}"
|
||||
if [ "$BUILD_ARCH" = "aarch64" ]; then
|
||||
ARCH="aarch64"
|
||||
else
|
||||
ARCH="x86_64"
|
||||
fi
|
||||
|
||||
RPMBUILD_DIR="$DIST_DIR/rpmbuild"
|
||||
|
||||
FULL_PACKAGE_NAME="$PACKAGE_NAME-$ARCH"
|
||||
PKGDIR="$DIST_DIR/rpmpkg/$FULL_PACKAGE_NAME"
|
||||
|
||||
cleanup() {
|
||||
# Delete the rpmbuild directory where the build took place.
|
||||
if [ -d "$RPMBUILD_DIR" ]; then
|
||||
rm -rf "$RPMBUILD_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run cleanup() when the script terminates (whether it succeeded or failed).
|
||||
trap cleanup EXIT
|
||||
|
||||
# Construct a directory that contains the build tree.
|
||||
rm -rf "$RPMBUILD_DIR"
|
||||
mkdir -p "$RPMBUILD_DIR"/{SPEC,RPMS}
|
||||
|
||||
# Move the RPM-specific package metadata into the package directory.
|
||||
VERSION="$GIT_RELEASE_TAG"
|
||||
RELEASE="1"
|
||||
sed \
|
||||
"s#@@CHANNEL_SUFFIX@@#$CHANNEL_SUFFIX#g; \
|
||||
s#@@BINARY_NAME@@#$BINARY_NAME#g; \
|
||||
s#@@VERSION@@#$VERSION#g; \
|
||||
s#@@RELEASE@@#$RELEASE#g; \
|
||||
s#@@ARCH@@#$ARCH#g; \
|
||||
s#@@WORKSPACEROOT@@#$WORKSPACE_ROOT_DIR#g; \
|
||||
s#@@BUNDLEID@@#$BUNDLE_ID#g; \
|
||||
s#@@REPO_NAME@@#$REPO_NAME#g; \
|
||||
s#@@RELEASE_CHANNEL@@#$RELEASE_CHANNEL#g" \
|
||||
< "$WORKSPACE_ROOT_DIR/resources/linux/rpm/$ARTIFACT/warp.spec.template" \
|
||||
> "$RPMBUILD_DIR/SPEC/warp.spec"
|
||||
|
||||
# Build the actual package.
|
||||
fakeroot rpmbuild -v -bb "$RPMBUILD_DIR/SPEC/warp.spec" --target="$ARCH" --define "_topdir $RPMBUILD_DIR"
|
||||
|
||||
# Copy the package to the output directory.
|
||||
RPM_NAME="$PACKAGE_NAME-$VERSION-$RELEASE.$ARCH.rpm"
|
||||
cp "$RPMBUILD_DIR/RPMS/$ARCH/$RPM_NAME" "$OUT_DIR"
|
||||
|
||||
# If we're running on GitHub, sign the package using our signing key that
|
||||
# should have already been added to the gpg-agent with a preset passphrase.
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
# Import our signing key's public key into rpm so the signature can be
|
||||
# verified.
|
||||
sudo rpm --import https://releases.warp.dev/linux/keys/warp.asc
|
||||
|
||||
# We use batch mode with tty-based pin entry to ensure that the build doesn't
|
||||
# hang waiting on user input if, for some reason, we failed to preset the
|
||||
# passphrase for the signing key in the gpg-agent cache.
|
||||
#
|
||||
# Additionally, until we know that this works, don't let an error here fail
|
||||
# the build.
|
||||
rpmsign \
|
||||
--verbose \
|
||||
--addsign --key-id "linux-maintainers@warp.dev" \
|
||||
--define "_gpg_sign_cmd_extra_args --batch --yes --pinentry-mode loopback" --define "_gpg_digest_algo sha256" \
|
||||
"$OUT_DIR/$RPM_NAME" \
|
||||
|| true
|
||||
fi
|
||||
|
||||
echo "Successfully built .rpm package at $OUT_DIR/$RPM_NAME!"
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install all dependencies required to build Warp on Linux.
|
||||
|
||||
set -e
|
||||
|
||||
# Define some control sequences for text formatting.
|
||||
red="\033[0;31m"
|
||||
reset="\033[0m"
|
||||
|
||||
# Install some development libraries that will be needed in the compilation
|
||||
# process.
|
||||
UNAME="$(uname -a)"
|
||||
if [[ "$(source /etc/os-release; echo $ID $ID_LIKE)" = *"debian"* ]]; then
|
||||
echo "⬇️ Installing build-time dependencies..."
|
||||
# Define the packages to install as a bash array so we can include
|
||||
# comments between lines.
|
||||
PACKAGES=(
|
||||
curl
|
||||
git
|
||||
# Install various core packages for development work on Linux.
|
||||
build-essential cmake pkg-config
|
||||
# Make it so that running `python` runs `python3`.
|
||||
python-is-python3
|
||||
# Development headers for various libraries, needed when compiling
|
||||
# certain Rust crate dependencies.
|
||||
libssl-dev libfreetype-dev libexpat1-dev libgit2-dev
|
||||
# libs needed for loading system fonts
|
||||
libfontconfig1-dev
|
||||
# Needed in wasm compilation for parsing the version of wasm-bindgen
|
||||
jq
|
||||
# Needed for compressing web bundles
|
||||
brotli
|
||||
# Needed for voice input
|
||||
libasound2-dev
|
||||
)
|
||||
sudo apt-get install -y "${PACKAGES[@]}"
|
||||
|
||||
# Install a modern version of protoc. The apt 'protobuf-compiler' package on
|
||||
# Ubuntu 20.04 ships protoc 3.6.1, which is too old for proto3 'optional'
|
||||
# fields (requires protoc >= 3.15).
|
||||
PROTOC_VERSION="25.1"
|
||||
case "$(uname -m)" in
|
||||
x86_64) PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-x86_64.zip" ;;
|
||||
aarch64) PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-aarch_64.zip" ;;
|
||||
*) echo "Unsupported architecture for protoc: $(uname -m)"; exit 1 ;;
|
||||
esac
|
||||
curl -fsSL -o /tmp/protoc.zip \
|
||||
"https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}"
|
||||
sudo unzip -o /tmp/protoc.zip -d /usr/local bin/protoc 'include/*'
|
||||
rm /tmp/protoc.zip
|
||||
else
|
||||
echo -e "⚠️ ${red}Unknown Linux distribution; necessary build dependencies may not be installed!${reset}"
|
||||
fi
|
||||
|
||||
# Install Rust.
|
||||
"$PWD"/script/install_rust
|
||||
|
||||
# Install various build-time dependencies through cargo.
|
||||
"$PWD"/script/install_cargo_build_deps
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Ensure the ~/.local/bin directory exists so we can download things into it.
|
||||
LOCAL_BIN="$HOME/.local/bin"
|
||||
mkdir -p "$LOCAL_BIN"
|
||||
|
||||
# A helper function to download an architecture-dependent AppImage and install
|
||||
# it in ~/.local/bin.
|
||||
#
|
||||
# Usage:
|
||||
# download_appimage $BINARY_NAME $DOWNLOAD_URL_BASE
|
||||
#
|
||||
# Args:
|
||||
# BINARY_NAME: The name of the AppImage-bundled binary we want to
|
||||
# download, e.g.: "appimagetool".
|
||||
# DOWNLOAD_URL_BASE: The URL to the directory containing the architecture-
|
||||
# specific AppImages.
|
||||
download_appimage() {
|
||||
BINARY_NAME="$1"
|
||||
DOWNLOAD_URL_BASE="$2"
|
||||
|
||||
APPIMAGE_NAME="$BINARY_NAME-$(uname -m).AppImage"
|
||||
|
||||
if [ -x "$(which "$BINARY_NAME")" ]; then
|
||||
echo "✅ Found $BINARY_NAME on your PATH."
|
||||
elif [ -x "$(which "$APPIMAGE_NAME")" ]; then
|
||||
APPIMAGE_PATH="$(which "$APPIMAGE_NAME")"
|
||||
APPIMAGE_PARENT_DIR="$(dirname "$APPIMAGE_PATH")"
|
||||
# Create a symlink so we can invoke it like a normal binary.
|
||||
ln -s "$APPIMAGE_PATH" "$APPIMAGE_PARENT_DIR/$BINARY_NAME"
|
||||
echo "✅ Found $APPIMAGE_NAME on your PATH; added $BINARY_NAME symlink."
|
||||
else
|
||||
echo "⬇️ Downloading $APPIMAGE_NAME..."
|
||||
APPIMAGE_PATH="$LOCAL_BIN/$APPIMAGE_NAME"
|
||||
curl -fL "$DOWNLOAD_URL_BASE/$APPIMAGE_NAME" --output "$APPIMAGE_PATH"
|
||||
chmod +x "$APPIMAGE_PATH"
|
||||
# Create a symlink so we can invoke it like a normal binary.
|
||||
ln -s "$APPIMAGE_PATH" "$LOCAL_BIN/$BINARY_NAME"
|
||||
fi
|
||||
}
|
||||
|
||||
# Install linuxdeploy, a helper for constructing an AppDir (and then invoking
|
||||
# appimagetool).
|
||||
download_appimage "linuxdeploy" "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/"
|
||||
|
||||
if [ ! -x "$(which linuxdeploy)" ]; then
|
||||
echo -e "⚠️ ${red}Please make sure that \"$LOCAL_BIN\" is on your PATH, otherwise some scripts may not work.${reset}"
|
||||
fi
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install all dependencies required to build and run Warp on Linux.
|
||||
|
||||
set -e
|
||||
|
||||
# Define some control sequences for text formatting.
|
||||
red="\033[0;31m"
|
||||
reset="\033[0m"
|
||||
|
||||
# Install build dependencies.
|
||||
"$PWD"/script/linux/install_build_deps
|
||||
|
||||
# Install additional runtime dependencies.
|
||||
UNAME="$(uname -a)"
|
||||
if [[ "$(source /etc/os-release; echo $ID $ID_LIKE)" = *"debian"* ]]; then
|
||||
echo "⬇️ Installing runtime dependencies..."
|
||||
# Define the packages to install as a bash array so we can include
|
||||
# comments between lines.
|
||||
PACKAGES=(
|
||||
locales
|
||||
# Runtime library to get font information.
|
||||
fontconfig
|
||||
# Runtime library for deflate compression.
|
||||
zlib1g
|
||||
# Runtime libraries for X11.
|
||||
libx11-6 libxcb1 libxi6 libxcursor1 libxkbcommon-x11-0
|
||||
# Runtime libraries for Wayland.
|
||||
libwayland-client0 libwayland-egl1
|
||||
# Open-source Vulkan drivers from the mesa project.
|
||||
mesa-vulkan-drivers
|
||||
# Runtime libraries for EGL.
|
||||
libegl1
|
||||
# Ensuring at least one cursor library for WSL.
|
||||
yaru-theme-icon
|
||||
)
|
||||
sudo apt-get install -y "${PACKAGES[@]}"
|
||||
else
|
||||
echo -e "⚠️ ${red}Unknown Linux distribution; necessary runtime dependencies may not be installed!${reset}"
|
||||
fi
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install all dependencies required to build, run, and test Warp on Linux.
|
||||
|
||||
set -e
|
||||
|
||||
# Define some control sequences for text formatting.
|
||||
red="\033[0;31m"
|
||||
reset="\033[0m"
|
||||
|
||||
# Install runtime dependencies.
|
||||
"$PWD"/script/linux/install_runtime_deps
|
||||
|
||||
# Install additional testing dependencies.
|
||||
UNAME="$(uname -a)"
|
||||
if [[ "$(source /etc/os-release; echo $ID $ID_LIKE)" = *"debian"* ]]; then
|
||||
echo "⬇️ Installing test dependencies..."
|
||||
# Define the packages to install as a bash array so we can include
|
||||
# comments between lines.
|
||||
PACKAGES=(
|
||||
# Install the zsh and fish shells.
|
||||
zsh fish
|
||||
# We run vim in some integration tests to test the altscreen.
|
||||
vim
|
||||
)
|
||||
sudo apt-get install -y "${PACKAGES[@]}"
|
||||
|
||||
# If gcloud is not already installed, install it so that we can run SSH integration tests.
|
||||
if [[ ! -x "$(command -v gcloud)" ]]; then
|
||||
echo "⬇️ Installing the gcloud CLI..."
|
||||
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||
curl -f https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install google-cloud-cli -y
|
||||
fi
|
||||
else
|
||||
echo -e "⚠️ ${red}Unknown Linux distribution; necessary test dependencies may not be installed!${reset}"
|
||||
fi
|
||||
|
||||
# Install various testing dependencies through cargo.
|
||||
"$PWD"/script/install_cargo_test_deps
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# linuxdeploy input plugin for restructuring the AppDir to match Warp's
|
||||
# standard Linux install layout.
|
||||
#
|
||||
# By default, linuxdeploy places the executable at usr/bin/ inside the AppDir.
|
||||
# This plugin moves it to opt/warpdotdev/<package>/ (matching deb/rpm/arch
|
||||
# package layout), creates a symlink from usr/bin/ to the new location, and
|
||||
# copies bundled resources alongside the binary.
|
||||
#
|
||||
# This means that extracting an AppImage produces the same filesystem layout
|
||||
# as installing a .deb or .rpm package.
|
||||
#
|
||||
# Required environment variables:
|
||||
# WARP_BINARY_NAME: Name of the binary (e.g. "warp-dev").
|
||||
# WARP_PACKAGE_NAME: Package directory name (e.g. "warp-terminal-dev").
|
||||
# WARP_BUNDLED_RESOURCES_DIR: Path to the staged resources directory to copy.
|
||||
|
||||
set -e
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--plugin-api-version)
|
||||
echo "0"
|
||||
exit 0
|
||||
;;
|
||||
--appdir)
|
||||
APPDIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$APPDIR" ]; then
|
||||
echo "ERROR: --appdir is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for var in WARP_BINARY_NAME WARP_PACKAGE_NAME WARP_BUNDLED_RESOURCES_DIR; do
|
||||
if [ -z "${!var}" ]; then
|
||||
echo "ERROR: $var environment variable is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -d "$WARP_BUNDLED_RESOURCES_DIR" ]; then
|
||||
echo "ERROR: Bundled resources directory does not exist: $WARP_BUNDLED_RESOURCES_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SRC_BIN="$APPDIR/usr/bin/$WARP_BINARY_NAME"
|
||||
if [ ! -f "$SRC_BIN" ]; then
|
||||
echo "ERROR: Expected binary not found at $SRC_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create the /opt install directory inside the AppDir.
|
||||
OPT_DIR="$APPDIR/opt/warpdotdev/$WARP_PACKAGE_NAME"
|
||||
mkdir -p "$OPT_DIR"
|
||||
|
||||
# Move the binary from usr/bin/ to opt/warpdotdev/<package>/.
|
||||
echo "Relocating binary to $OPT_DIR/$WARP_BINARY_NAME"
|
||||
mv "$SRC_BIN" "$OPT_DIR/$WARP_BINARY_NAME"
|
||||
|
||||
# Create a relative symlink so usr/bin/<name> still resolves to the binary.
|
||||
# From usr/bin/ to opt/warpdotdev/<package>/ is ../../opt/warpdotdev/<package>/.
|
||||
echo "Creating symlink at usr/bin/$WARP_BINARY_NAME"
|
||||
ln -s "../../opt/warpdotdev/$WARP_PACKAGE_NAME/$WARP_BINARY_NAME" "$SRC_BIN"
|
||||
|
||||
# Copy bundled resources alongside the binary.
|
||||
DEST_RESOURCES="$OPT_DIR/resources"
|
||||
echo "Copying bundled resources to $DEST_RESOURCES"
|
||||
mkdir -p "$DEST_RESOURCES"
|
||||
cp -R "$WARP_BUNDLED_RESOURCES_DIR/." "$DEST_RESOURCES/"
|
||||
|
||||
echo "Successfully restructured AppDir for Warp"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Signs the Arch Linux packages in a given directory.
|
||||
|
||||
PKGDIR="$1"
|
||||
|
||||
for package in $PKGDIR/*.pkg.tar.zst; do
|
||||
gpg --no-tty --pinentry-mode loopback --detach-sign --no-armor --batch --yes --output $package.sig $package
|
||||
done
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Run this script once to prepare your machine to develop Warp. This script is
|
||||
# a work in progress, and may be incomplete.
|
||||
|
||||
set -e
|
||||
|
||||
if ! [ -d "/Applications/Xcode.app" ]; then
|
||||
echo "Please install Xcode from the App Store before continuing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
# Mimic actually launching XCode, which performs some necessary set-up of the
|
||||
# development environment.
|
||||
xcodebuild -runFirstLaunch
|
||||
|
||||
if ! command -v brew; then
|
||||
echo "Installing brew..."
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo "Please make sure brew is set correctly in your PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v cargo; then
|
||||
echo "Installing rust..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
echo "Please start a new terminal session so that cargo is in your PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install various binaries through cargo.
|
||||
"$PWD"/script/install_cargo_test_deps
|
||||
"$PWD"/script/install_cargo_release_deps
|
||||
|
||||
# Install a version of cargo-bundle that supports the --profile flag
|
||||
cargo install cargo-bundle --git=https://github.com/burtonageo/cargo-bundle --rev ae4c76e92c08774bf54ff077b1c52e3d1cd6c16d
|
||||
|
||||
# Update brew
|
||||
brew update
|
||||
|
||||
brew install jq
|
||||
brew install getsentry/tools/sentry-cli
|
||||
brew install clang-format
|
||||
brew install create-dmg
|
||||
brew install multitime
|
||||
brew install powershell
|
||||
brew install pkgconf
|
||||
brew install llvm
|
||||
|
||||
# Install PSScriptAnalyzer for PowerShell linting
|
||||
pwsh -Command "Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser"
|
||||
|
||||
if ! [ "$(command -v docker)" ]; then
|
||||
brew install --cask docker
|
||||
fi
|
||||
|
||||
if ! [ "$(command -v gcloud)" ]; then
|
||||
brew install google-cloud-sdk
|
||||
fi
|
||||
|
||||
if [[ -z $(gcloud auth print-identity-token) ]]; then
|
||||
echo "gcloud CLI authentication missing. Press enter to continue..."
|
||||
read var
|
||||
gcloud auth login
|
||||
fi
|
||||
|
||||
# Needed for building for Mac ARM machines (e.g. M1)
|
||||
rustup target add aarch64-apple-darwin
|
||||
Executable
+823
@@ -0,0 +1,823 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Bundle the application for distribution.
|
||||
#
|
||||
# See the parameter parsing section below for available options.
|
||||
#
|
||||
# Note that there are several steps to complete before the app is ready to
|
||||
# be distributed, one of which is asynchronous and requires apple to process
|
||||
# our binary.
|
||||
#
|
||||
# The exact steps depend on which artifact we are building, the desktop app or the CLI. Overall,
|
||||
# the steps are:
|
||||
#
|
||||
# 1) Create the binary or mac bundle by running `cargo`.
|
||||
# 2) Create a keychain based on our distribution cert.
|
||||
# 3) Codesign the built artifact using the keychain.
|
||||
#
|
||||
# Additionally, for an app, we create a dmg:
|
||||
# 4) Create the dmg using `hdiutil create`.
|
||||
# 5) Codesign our dmg using the keychain.
|
||||
# 6) Upload the app to Apple for it to be notarized - this is async, and we
|
||||
# poll until it is done.
|
||||
# 7) "Staple" the notarization to the dmg.
|
||||
#
|
||||
# Once the stapling is done, the app can be shared.
|
||||
#
|
||||
# Three passwords are read from GCP Secret Manager (or from env if --read-passwords-from-env is set) if you are codesigning.
|
||||
# 1) WARP_NOTARIZATION_PASSWORD: This is an "app-specific password" that
|
||||
# is tied to the zach@warp.dev account. See https://support.apple.com/en-us/HT204397
|
||||
# 2) WARP_DEVELOPER_ID_CERT_PASSWORD: This is a password tied to the private key
|
||||
# of our cert - it is needed to use the cert to sign our binary.
|
||||
# 3) WARP_CODESIGN_KEYCHAIN_PASSWORD: This is an arbitrary password only used
|
||||
# in the lifetime of this app for creating the keychain used to sign. Can
|
||||
# be anything.
|
||||
#
|
||||
# See
|
||||
# https://github.com/burtonageo/cargo-bundle
|
||||
# https://wiki.lazarus.freepascal.org/Code_Signing_for_macOS
|
||||
# https://wiki.lazarus.freepascal.org/Notarization_for_macOS_10.14.5%2B
|
||||
# https://developer.apple.com/developer-id/
|
||||
# https://github.com/atom/atom/blob/976cb9ef3a611163052f9d31c6c3685dc1e6c5b4/script/lib/code-sign-on-mac.js
|
||||
|
||||
set -e
|
||||
|
||||
# Determine the repository root directory
|
||||
WORKSPACE_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
# This is used to set the minimum deployment target for mac
|
||||
# https://cmake.org/cmake/help/latest/envvar/MACOSX_DEPLOYMENT_TARGET.html
|
||||
# If unset, it uses the system one, but we want to build for older versions
|
||||
# of mac os by default.
|
||||
#
|
||||
# This should be kept in sync with the definition of this variable in
|
||||
# `.cargo/config.toml`.
|
||||
export MACOSX_DEPLOYMENT_TARGET="10.14"
|
||||
|
||||
# Constants used later in the script.
|
||||
INTEL_ARCH="x86_64"
|
||||
INTEL_TARGET="$INTEL_ARCH-apple-darwin"
|
||||
ARM_ARCH="aarch64"
|
||||
ARM_TARGET="$ARM_ARCH-apple-darwin"
|
||||
DEFAULT_ARCH="$(rustc --print cfg | grep target_arch)"
|
||||
|
||||
# Function to clean up temporary DMG files
|
||||
cleanup_dmg_files() {
|
||||
local target_dir="$1"
|
||||
if [ -d "$target_dir" ]; then
|
||||
echo "Cleaning up temporary DMG files in $target_dir"
|
||||
find "$target_dir" -name "*.dmg" -type f -delete
|
||||
find "$target_dir" -name "rw.*.dmg" -type f -delete
|
||||
fi
|
||||
# Also check if any volumes are mounted and unmount them
|
||||
hdiutil info | grep "/Volumes/Warp.*" | awk '{print $1}' | while read -r disk; do
|
||||
echo "Unmounting disk image: $disk"
|
||||
hdiutil detach "$disk" -force || true
|
||||
done
|
||||
}
|
||||
|
||||
# Clean up the temporary codesigning keychain.
|
||||
cleanup_codesign_keychain() {
|
||||
if [[ "${CODESIGN:-false}" = true && -n "${CODESIGN_KEYCHAIN_NAME:-}" ]]; then
|
||||
echo "Cleaning up by deleting $CODESIGN_KEYCHAIN_NAME keychain."
|
||||
security delete-keychain "$CODESIGN_KEYCHAIN_NAME" || echo "No keychain to delete or already deleted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Set up cleanup trap
|
||||
trap 'cleanup_dmg_files "$DMG_DIR"; cleanup_codesign_keychain' EXIT
|
||||
|
||||
# Define a helper function that uses Python to compute a relative path.
|
||||
function relpath() {
|
||||
python -c "import os,sys;print(os.path.relpath(*(sys.argv[1:])))" "$@";
|
||||
}
|
||||
|
||||
# Defaults for command-line flags.
|
||||
UNIVERSAL_BINARY=true
|
||||
BUILD_BINARY=true
|
||||
TARGET_ARCH=""
|
||||
DMG_NAME_SUFFIX=""
|
||||
# By default we build dev bundles.
|
||||
RELEASE_CHANNEL="dev"
|
||||
FEATURES="release_bundle,cocoa_sentry,extern_plist"
|
||||
REGISTER_SERVICES=true
|
||||
DEBUG=false
|
||||
ARTIFACT="app"
|
||||
CODESIGN=true
|
||||
SELFSIGN=false
|
||||
OPEN_AFTER_BUNDLE=false
|
||||
READ_PASSWORDS_FROM_ENV=false
|
||||
|
||||
PARAMS=""
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
# Speed up compilation time by only compiling a debug version of the app,
|
||||
# rather than a release version
|
||||
--debug)
|
||||
DEBUG=true
|
||||
shift
|
||||
;;
|
||||
# Only run `cargo check` without producing a bundle
|
||||
--check-only)
|
||||
echo 'Only running `cargo check` and not producing a bundle.'
|
||||
CHECK_ONLY="true"
|
||||
shift
|
||||
;;
|
||||
# Skip building the binary (assume it's already built)
|
||||
--skip-build)
|
||||
echo "Skipping binary build step."
|
||||
BUILD_BINARY=false
|
||||
shift
|
||||
;;
|
||||
# Skip code signing process
|
||||
--nosign)
|
||||
echo "Skipping code signing."
|
||||
CODESIGN=false
|
||||
SELFSIGN=false
|
||||
shift
|
||||
;;
|
||||
# Sign with a local Apple Development cert instead of the official Warp cert.
|
||||
# Useful for local debug builds when you don't have access to the company signing key.
|
||||
# Falls back to ad-hoc signing if no Apple Development cert is found.
|
||||
--selfsign)
|
||||
echo "Self-signing enabled."
|
||||
CODESIGN=false
|
||||
SELFSIGN=true
|
||||
shift
|
||||
;;
|
||||
# Build only for the default target architecture instead of universal binary
|
||||
--nouniversal)
|
||||
echo "Only building for default target $DEFAULT_TARGET, not a universal binary."
|
||||
UNIVERSAL_BINARY=false
|
||||
shift
|
||||
;;
|
||||
# Build only for a specific architecture (implies --nouniversal)
|
||||
--arch)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
if [ "$2" = "x86_64" -o "$2" = "aarch64" ]; then
|
||||
echo "Building for specific architecture: $2"
|
||||
TARGET_ARCH=$2
|
||||
UNIVERSAL_BINARY=false
|
||||
shift 2
|
||||
else
|
||||
echo "Error: --arch must be either x86_64 or aarch64, got '$2'" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
# Set a custom suffix for the DMG file
|
||||
--dmg-name-suffix)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
echo "Setting custom DMG name suffix to $2"
|
||||
DMG_NAME_SUFFIX=$2
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
# Open the parent directory after bundling completes.
|
||||
-o|--open)
|
||||
OPEN_AFTER_BUNDLE=true
|
||||
shift
|
||||
;;
|
||||
# Specify the release channel (local, dev, preview, stable)
|
||||
-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
|
||||
;;
|
||||
# Set a specific Git release tag for the build
|
||||
--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
|
||||
;;
|
||||
# Read codesigning passwords from environment variables instead of GCP secret manager
|
||||
--read-passwords-from-env)
|
||||
echo "Reading codesigning passwords from env."
|
||||
READ_PASSWORDS_FROM_ENV=true
|
||||
shift
|
||||
;;
|
||||
--features)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
echo "Adding Cargo features: $2"
|
||||
FEATURES="$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" ]]; 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
|
||||
;;
|
||||
*) # preserve positional arguments
|
||||
PARAMS="$PARAMS $1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# set positional arguments in their proper place
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
# Infer a cargo profile from the bundle configuration.
|
||||
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).
|
||||
CARGO_PROFILE="release-lto-debug_assertions"
|
||||
else
|
||||
CARGO_PROFILE="release-lto"
|
||||
fi
|
||||
|
||||
TARGET_PROFILE_DIR="$CARGO_PROFILE"
|
||||
if [[ "$CARGO_PROFILE" == "dev" ]]; then
|
||||
TARGET_PROFILE_DIR="debug"
|
||||
fi
|
||||
|
||||
if [[ $RELEASE_CHANNEL = "local" ]]; then
|
||||
WARP_BIN="warp"
|
||||
BUNDLE_ID="dev.warp.Warp-Local"
|
||||
WARP_APP_NAME="WarpLocal"
|
||||
WARP_SCHEME_NAME="warplocal"
|
||||
FEATURES="$FEATURES,agent_mode_debug"
|
||||
# For local builds, use different versions of our bundled frameworks (e.g.:
|
||||
# Sentry). This needs to be exported so it can be referenced by
|
||||
# app/build.rs later, while running `cargo bundle`.
|
||||
export FRAMEWORK_OVERRIDE="dev"
|
||||
elif [[ $RELEASE_CHANNEL = "dev" ]]; then
|
||||
WARP_BIN="dev"
|
||||
BUNDLE_ID="dev.warp.Warp-Dev"
|
||||
WARP_APP_NAME="WarpDev"
|
||||
WARP_SCHEME_NAME="warpdev"
|
||||
FEATURES="$FEATURES,agent_mode_debug"
|
||||
# Enable heap usage tracking & profiling using jemalloc through pprof.
|
||||
FEATURES="$FEATURES,jemalloc_pprof,heap_usage_tracking"
|
||||
# For dev builds, use different versions of our bundled frameworks (e.g.:
|
||||
# Sentry). This needs to be exported so it can be referenced by
|
||||
# app/build.rs later, while running `cargo bundle`.
|
||||
export FRAMEWORK_OVERRIDE="dev"
|
||||
export HANDLE_MARKDOWN=1
|
||||
elif [[ $RELEASE_CHANNEL = "preview" ]]; then
|
||||
WARP_BIN="preview"
|
||||
BUNDLE_ID="dev.warp.Warp-Preview"
|
||||
WARP_APP_NAME="WarpPreview"
|
||||
WARP_SCHEME_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"
|
||||
BUNDLE_ID="dev.warp.Warp-Stable"
|
||||
WARP_APP_NAME="Warp"
|
||||
WARP_SCHEME_NAME="warp"
|
||||
fi
|
||||
|
||||
OUT_DIR="target/$TARGET_PROFILE_DIR/bundle/osx"
|
||||
DOCK_TILE_PLUGIN_DIR="target/$TARGET_PROFILE_DIR/WarpDockTilePlugin.docktileplugin"
|
||||
|
||||
# Handle specific architecture targeting
|
||||
if [[ -n "$TARGET_ARCH" ]]; then
|
||||
# Building for a specific architecture
|
||||
if [[ "$TARGET_ARCH" == "$INTEL_ARCH" ]]; then
|
||||
echo "Building specifically for $INTEL_TARGET"
|
||||
DEFAULT_TARGET="$INTEL_TARGET"
|
||||
BUNDLE_DIR="target/$INTEL_TARGET/$TARGET_PROFILE_DIR/bundle/osx"
|
||||
elif [[ "$TARGET_ARCH" == "$ARM_ARCH" ]]; then
|
||||
echo "Building specifically for $ARM_TARGET"
|
||||
DEFAULT_TARGET="$ARM_TARGET"
|
||||
BUNDLE_DIR="target/$ARM_TARGET/$TARGET_PROFILE_DIR/bundle/osx"
|
||||
fi
|
||||
else
|
||||
# Auto-detect default target based on current architecture
|
||||
if [[ "$DEFAULT_ARCH" == *"$INTEL_ARCH"* ]]; then
|
||||
echo "Default target is $INTEL_TARGET"
|
||||
DEFAULT_TARGET="$INTEL_TARGET"
|
||||
ADDITIONAL_TARGET="$ARM_TARGET"
|
||||
BUNDLE_DIR="target/$INTEL_TARGET/$TARGET_PROFILE_DIR/bundle/osx"
|
||||
elif [[ "$DEFAULT_ARCH" == *"$ARM_ARCH"* ]]; then
|
||||
echo "Default target is $ARM_TARGET"
|
||||
DEFAULT_TARGET="$ARM_TARGET"
|
||||
ADDITIONAL_TARGET="$INTEL_TARGET"
|
||||
BUNDLE_DIR="target/$ARM_TARGET/$TARGET_PROFILE_DIR/bundle/osx"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set artifact-specific configuration.
|
||||
if [[ "$ARTIFACT" == cli ]]; then
|
||||
UNIVERSAL_BINARY=false
|
||||
OPEN_AFTER_BUNDLE=false
|
||||
FEATURES="$FEATURES,standalone"
|
||||
elif [[ "$ARTIFACT" == app ]]; then
|
||||
FEATURES="$FEATURES,gui,nld_improvements"
|
||||
fi
|
||||
|
||||
# If we're building a universal bundle for the app artifact, make sure the additional target is available.
|
||||
if [[ $UNIVERSAL_BINARY = true ]]; then
|
||||
rustup target add "$ADDITIONAL_TARGET"
|
||||
fi
|
||||
|
||||
# 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 --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --target "$DEFAULT_TARGET" --features "$FEATURES"
|
||||
if [[ $UNIVERSAL_BINARY = true && "$ARTIFACT" != "cli" ]]; then
|
||||
cargo check --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --target "$ADDITIONAL_TARGET" --features "$FEATURES"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DMG_DIR="$BUNDLE_DIR/dmg/$WARP_BIN"
|
||||
DMG_NAME="$WARP_APP_NAME.dmg"
|
||||
if [[ -z "$FINAL_DMG_NAME" && -n "$DMG_NAME_SUFFIX" ]]; then
|
||||
FINAL_DMG_NAME="$WARP_APP_NAME-$DMG_NAME_SUFFIX.dmg"
|
||||
else
|
||||
FINAL_DMG_NAME="$WARP_APP_NAME.dmg"
|
||||
fi
|
||||
|
||||
# First clean up and prep the outdir
|
||||
mkdir -p "$OUT_DIR"
|
||||
rm -R "$OUT_DIR/$WARP_APP_NAME.app" || echo "No old app to remove"
|
||||
rm -R "$OUT_DIR/$FINAL_DMG_NAME" || echo "No old dmg to remove"
|
||||
|
||||
###########################
|
||||
## Step 1: Build the app ##
|
||||
###########################
|
||||
|
||||
if [[ "$ARTIFACT" == "app" ]]; then
|
||||
if [[ $BUILD_BINARY != true ]]; then
|
||||
echo "Skipping binary build due to --skip-build flag"
|
||||
export CARGO_BUNDLE_SKIP_BUILD=1
|
||||
fi
|
||||
|
||||
pushd app > /dev/null
|
||||
echo "Building and bundling $DEFAULT_TARGET for channel $RELEASE_CHANNEL and bundle id $BUNDLE_ID with profile $CARGO_PROFILE"
|
||||
cargo bundle --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --target "$DEFAULT_TARGET" --features "$FEATURES"
|
||||
popd > /dev/null
|
||||
|
||||
echo "Adding rpath to support mac frameworks (e.g. Sentry)"
|
||||
install_name_tool -add_rpath "@executable_path/../Frameworks" "$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/MacOS/$WARP_BIN"
|
||||
|
||||
export WARP_SCHEME_NAME
|
||||
export WARP_PLIST_PATH="$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/Info.plist"
|
||||
./script/update_plist
|
||||
|
||||
if [[ $REGISTER_SERVICES = true ]]; then
|
||||
plutil -insert NSServices -xml "
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSMenuItem</key>
|
||||
<dict>
|
||||
<key>default</key>
|
||||
<string>New $WARP_APP_NAME Tab Here</string>
|
||||
</dict>
|
||||
<key>NSMessage</key>
|
||||
<string>openTab</string>
|
||||
<key>NSRequiredContext</key>
|
||||
<dict>
|
||||
<key>NSTextContent</key>
|
||||
<string>FilePath</string>
|
||||
</dict>
|
||||
<key>NSSendTypes</key>
|
||||
<array>
|
||||
<string>NSFilenamesPboardType</string>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSMenuItem</key>
|
||||
<dict>
|
||||
<key>default</key>
|
||||
<string>New $WARP_APP_NAME Window Here</string>
|
||||
</dict>
|
||||
<key>NSMessage</key>
|
||||
<string>openWindow</string>
|
||||
<key>NSRequiredContext</key>
|
||||
<dict>
|
||||
<key>NSTextContent</key>
|
||||
<string>FilePath</string>
|
||||
</dict>
|
||||
<key>NSSendTypes</key>
|
||||
<array>
|
||||
<string>NSFilenamesPboardType</string>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>" "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/Info.plist
|
||||
fi
|
||||
|
||||
# Temporary key that ChatGPT Desktop can use to determine if the latest version of Warp supports the ChatGPT integration.
|
||||
# Once support has been rolled out for a sufficient amount of time we (and ChatGPT) can remove this.
|
||||
plutil -insert SUPPORTS_CHAT_GPT_WORK_WITH_APPS -bool true "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/Info.plist
|
||||
|
||||
# Add LSBackgroundOnly key and set it to false since we need a UI app
|
||||
plutil -insert LSBackgroundOnly -bool false "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/Info.plist
|
||||
|
||||
# Add SMAuthorizedClients for macOS 13+ (Ventura) to support login item functionality
|
||||
plutil -insert SMAuthorizedClients -xml "<array><string>$BUNDLE_ID</string></array>" "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/Info.plist
|
||||
|
||||
if [[ $UNIVERSAL_BINARY = true ]]; then
|
||||
if [[ $BUILD_BINARY = true ]]; then
|
||||
echo "Building $ADDITIONAL_TARGET to include in universal binary"
|
||||
|
||||
pushd app > /dev/null
|
||||
cargo build --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --target "$ADDITIONAL_TARGET" --features "$FEATURES"
|
||||
popd > /dev/null
|
||||
else
|
||||
echo "Skipping build of $ADDITIONAL_TARGET due to --skip-build flag"
|
||||
fi
|
||||
|
||||
tmp_bundle_dir=$(mktemp -d -t ci-XXXXXXXXXX)
|
||||
|
||||
echo "Adding rpath to both binaries for universal executable"
|
||||
install_name_tool -add_rpath "@executable_path/../Frameworks" "target/$INTEL_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN"
|
||||
install_name_tool -add_rpath "@executable_path/../Frameworks" "target/$ARM_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN"
|
||||
|
||||
echo "Building universal binary using lipo."
|
||||
lipo -create -output "$tmp_bundle_dir/$WARP_BIN" \
|
||||
"target/$INTEL_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN" \
|
||||
"target/$ARM_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN"
|
||||
|
||||
# Compute the real absolute path to the dSYM file, factoring in symlinks.
|
||||
# Starting in Rust 1.56, the generated dSYMs are symlinks to files within the target/dep directory. For example,
|
||||
# a dSYM for dev may be symlinked to target/deps/dev-080cf7e291fb066d.dSYM. This means the actual debug symbols are
|
||||
# would be located at dev.dSYM/Contents/Resources/DWARF/dev-080cf7e291fb066d.dSYM where dev.dSYM is symlink to
|
||||
# deps/dev-080cf7e291fb066d.dSYM. To fix this, parse out the actual name of the directory that the dSYM is
|
||||
# symlinked to, since this is also the name of file that contains the debug symbols within the dSYM.
|
||||
INTEL_DSYM_NAME=$(basename "$(realpath target/$INTEL_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM)" .dSYM)
|
||||
INTEL_DSYM_PATH="target/$INTEL_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM/Contents/Resources/DWARF/$INTEL_DSYM_NAME"
|
||||
ARM_DSYM_NAME=$(basename "$(realpath target/$ARM_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM)" .dSYM)
|
||||
ARM_DSYM_PATH="target/$ARM_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM/Contents/Resources/DWARF/$ARM_DSYM_NAME"
|
||||
if [[ -e "$INTEL_DSYM_PATH" && -e "$ARM_DSYM_PATH" ]]; then
|
||||
echo "Building universal binary .dSYM using lipo and storing in $OUT_DIR/$WARP_BIN.dSYM"
|
||||
|
||||
# Use lipo to merge the .dSYM files into a single universal .dSYM file.
|
||||
# It expects the base name for the file (with the .dSYM suffix removed).
|
||||
lipo -create -output "$OUT_DIR/$WARP_BIN.dSYM" "${INTEL_DSYM_PATH}" "${ARM_DSYM_PATH}"
|
||||
fi
|
||||
|
||||
echo "Storing result in $BUNDLE_DIR, replacing $DEFAULT_TARGET binary with fat binary."
|
||||
mv "$tmp_bundle_dir/$WARP_BIN" "$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/MacOS"
|
||||
elif [[ -n "$TARGET_ARCH" && -e "target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM" ]]; then
|
||||
echo "Copying .dSYM into $OUT_DIR/$WARP_BIN.dSYM"
|
||||
cp -HR "target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM" "$OUT_DIR/"
|
||||
fi
|
||||
|
||||
# Note that the dock tile plugin is pre-built for both arm64 and x86_64 so we don't need to run lipo on it.
|
||||
echo "Creating PlugIns directory and copying pre-built DockTilePlugin..."
|
||||
mkdir -p "$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/PlugIns"
|
||||
cp -R "$DOCK_TILE_PLUGIN_DIR" "$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/PlugIns/"
|
||||
|
||||
echo "Updating plist with dock tile plugin entries"
|
||||
plutil -insert NSDockTilePlugIn -string "WarpDockTilePlugin.docktileplugin" "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/Info.plist
|
||||
plutil -insert MainAppBundleIdentifier -string "$BUNDLE_ID" "$BUNDLE_DIR"/$WARP_APP_NAME.app/Contents/PlugIns/WarpDockTilePlugin.docktileplugin/Contents/Info.plist
|
||||
|
||||
BUNDLED_RESOURCES_DIR="$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/Resources"
|
||||
echo "Preparing bundled resources..."
|
||||
"$WORKSPACE_ROOT_DIR/script/prepare_bundled_resources" "$BUNDLED_RESOURCES_DIR" "$RELEASE_CHANNEL" "$CARGO_PROFILE"
|
||||
|
||||
"$WORKSPACE_ROOT_DIR/script/compile_icon" "$RELEASE_CHANNEL" "$BUNDLE_DIR/$WARP_APP_NAME.app"
|
||||
|
||||
HELPERS_DIR="$BUNDLE_DIR/$WARP_APP_NAME.app/Contents/Helpers"
|
||||
if [[ ",$FEATURES," =~ ",heap_usage_tracking," ]]; then
|
||||
echo "Bundling pprof..."
|
||||
"$WORKSPACE_ROOT_DIR/script/prepare_bundled_pprof" "$HELPERS_DIR"
|
||||
fi
|
||||
|
||||
# Determine CLI wrapper script path based on release channel
|
||||
if [[ $RELEASE_CHANNEL = "stable" ]]; then
|
||||
CLI_SCRIPT_PATH="$BUNDLED_RESOURCES_DIR/bin/oz"
|
||||
else
|
||||
CLI_SCRIPT_PATH="$BUNDLED_RESOURCES_DIR/bin/oz-$RELEASE_CHANNEL"
|
||||
fi
|
||||
|
||||
echo "Creating Resources/bin directory and CLI wrapper script..."
|
||||
mkdir -p "$BUNDLED_RESOURCES_DIR/bin"
|
||||
|
||||
cat > "$CLI_SCRIPT_PATH" << 'EOF'
|
||||
#!/bin/bash
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec -a "$0" "$script_dir/../../MacOS/WARP_BIN_PLACEHOLDER" "$@"
|
||||
EOF
|
||||
|
||||
# Replace the placeholder with the actual binary name
|
||||
sed -i '' "s/WARP_BIN_PLACEHOLDER/$WARP_BIN/" "$CLI_SCRIPT_PATH"
|
||||
|
||||
# Make the script executable
|
||||
chmod +x "$CLI_SCRIPT_PATH"
|
||||
|
||||
# Store the built artifact locations for GitHub Actions outputs.
|
||||
BINARY_PATH="target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN"
|
||||
DMG_PATH="$OUT_DIR/$FINAL_DMG_NAME"
|
||||
elif [[ "$ARTIFACT" == "cli" ]]; then
|
||||
if [[ $BUILD_BINARY == true ]]; then
|
||||
# Create Info.plist before building the app, since it's embedded at build time.
|
||||
# Apple's codesigning tools will detect Info.plist files in the same directory as an executable.
|
||||
# This breaks the code signature, so we must use a different location for the file.
|
||||
mkdir -p "$BUNDLE_DIR"
|
||||
export WARP_PLIST_PATH="$BUNDLE_DIR/cli-info.plist"
|
||||
cp app/assets/resources/mac/CLI-Info.plist "$WARP_PLIST_PATH"
|
||||
|
||||
export WARP_PLIST_NO_FILE_TYPES=true
|
||||
./script/update_plist
|
||||
plutil -insert CFBundleIdentifier -string "$BUNDLE_ID" "$WARP_PLIST_PATH"
|
||||
plutil -insert CFBundleName -string "$WARP_BIN" "$WARP_PLIST_PATH"
|
||||
plutil -insert CFBundleExecutable -string "$WARP_BIN" "$WARP_PLIST_PATH"
|
||||
|
||||
export "INFO_PLIST_PATH=$(realpath "$WARP_PLIST_PATH")"
|
||||
pushd app > /dev/null
|
||||
echo "Building $DEFAULT_TARGET for channel $RELEASE_CHANNEL with profile $CARGO_PROFILE"
|
||||
cargo build --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --target "$DEFAULT_TARGET" --features "$FEATURES"
|
||||
popd > /dev/null
|
||||
else
|
||||
echo "Skipping binary build due to --skip-build flag"
|
||||
fi
|
||||
|
||||
echo "Copying binary into $OUT_DIR/$WARP_BIN"
|
||||
cp "target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN" "$OUT_DIR/$WARP_BIN"
|
||||
|
||||
if [[ -n "$TARGET_ARCH" && -e "target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM" ]]; then
|
||||
echo "Copying .dSYM into $OUT_DIR/$WARP_BIN.dSYM"
|
||||
cp -HR "target/$DEFAULT_TARGET/$TARGET_PROFILE_DIR/$WARP_BIN.dSYM" "$OUT_DIR/"
|
||||
fi
|
||||
|
||||
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"
|
||||
|
||||
|
||||
# Set the primary binary path to output.
|
||||
BINARY_PATH="$OUT_DIR/$WARP_BIN"
|
||||
else
|
||||
echo "Unsupported artifact: $ARTIFACT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
##########################################
|
||||
## Step 2: Create code-signing keychain ##
|
||||
##########################################
|
||||
|
||||
if [[ $READ_PASSWORDS_FROM_ENV != true ]]; then
|
||||
echo "Skipping code-signing because passwords are not available in environment."
|
||||
CODESIGN=false
|
||||
fi
|
||||
|
||||
if [[ $CODESIGN = true ]]; then
|
||||
# TODO - does this need to change per user? Seems like it's tied to the WARP_NOTARIZATION_PASSWORD password.
|
||||
APPLE_TEAM_ID="2BBY89MBSN"
|
||||
CODESIGN_KEYCHAIN_NAME="warp-codesign-keychain"
|
||||
|
||||
echo "Starting codesigning..."
|
||||
|
||||
if [[ $READ_PASSWORDS_FROM_ENV = true ]]; then
|
||||
if [ -z "$WARP_NOTARIZATION_PASSWORD" ] ; then
|
||||
echo "WARP_NOTARIZATION_PASSWORD must be set for code signing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$WARP_DEVELOPER_ID_CERT_PASSWORD" ] ; then
|
||||
echo "WARP_DEVELOPER_ID_CERT_PASSWORD must be set for code signing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$WARP_CODESIGN_KEYCHAIN_PASSWORD" ] ; then
|
||||
echo "WARP_CODESIGN_KEYCHAIN_PASSWORD must be set for code signing"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
security delete-keychain $CODESIGN_KEYCHAIN_NAME || echo "No existing keychain to clean up".
|
||||
|
||||
echo "Creating $CODESIGN_KEYCHAIN_NAME keychain."
|
||||
security create-keychain -p "$WARP_CODESIGN_KEYCHAIN_PASSWORD" $CODESIGN_KEYCHAIN_NAME
|
||||
security list-keychains -s $CODESIGN_KEYCHAIN_NAME
|
||||
security set-keychain-settings -t 3600 -u $CODESIGN_KEYCHAIN_NAME
|
||||
|
||||
echo "Unlocking keychain and setting cert."
|
||||
security unlock-keychain -p "$WARP_CODESIGN_KEYCHAIN_PASSWORD" $CODESIGN_KEYCHAIN_NAME
|
||||
security import <(echo "$WARP_DEVELOPER_ID_CERT" | base64 -d) -f pkcs12 -P "$WARP_DEVELOPER_ID_CERT_PASSWORD" -k $CODESIGN_KEYCHAIN_NAME -T /usr/bin/codesign
|
||||
security set-key-partition-list -S "apple-tool:,apple:" -s -k "$WARP_CODESIGN_KEYCHAIN_PASSWORD" $CODESIGN_KEYCHAIN_NAME
|
||||
fi
|
||||
|
||||
##############################
|
||||
## Step 3: Codesign the app ##
|
||||
##############################
|
||||
|
||||
if [[ $SELFSIGN = true ]]; then
|
||||
SIGNING_CERT="$(security find-identity -p codesigning -v | grep "Apple Development" | awk '{print $2}' | head -1)"
|
||||
if [[ -z "$SIGNING_CERT" ]]; then
|
||||
echo "No Apple Development cert found, falling back to ad-hoc signing."
|
||||
SIGNING_CERT="-"
|
||||
else
|
||||
echo "Found Apple Development certificate"
|
||||
fi
|
||||
if [[ "$ARTIFACT" == app ]]; then
|
||||
echo "Self-signing $BUNDLE_DIR/$WARP_APP_NAME.app with ${SIGNING_CERT}..."
|
||||
codesign --force --deep --options runtime --sign "$SIGNING_CERT" "$BUNDLE_DIR/$WARP_APP_NAME.app" --entitlements script/Debug-Entitlements.plist
|
||||
elif [[ "$ARTIFACT" == cli ]]; then
|
||||
echo "Self-signing $OUT_DIR/$WARP_BIN with ${SIGNING_CERT}..."
|
||||
codesign --force --options runtime --sign "$SIGNING_CERT" "$OUT_DIR/$WARP_BIN" --entitlements script/Debug-Entitlements.plist
|
||||
fi
|
||||
elif [[ $CODESIGN = true ]]; then
|
||||
if [[ "$ARTIFACT" == app ]]; then
|
||||
echo "Codesigning $BUNDLE_DIR/$WARP_APP_NAME.app..."
|
||||
# Use --deep so we sign bundled frameworks as well
|
||||
codesign --deep -f -o runtime --timestamp -s "$APPLE_TEAM_ID" "$BUNDLE_DIR/$WARP_APP_NAME.app" --entitlements script/Entitlements.plist
|
||||
elif [[ "$ARTIFACT" == cli ]]; then
|
||||
echo "Codesigning $OUT_DIR/$WARP_BIN..."
|
||||
codesign -f -o runtime --timestamp -s "$APPLE_TEAM_ID" "$OUT_DIR/$WARP_BIN" --entitlements script/Entitlements.plist
|
||||
|
||||
# Create the .zip for notarization in a separate location - otherwise, Apple's codesigning
|
||||
# tools decide that it's a sealed resource that belongs to the binary and needs to also be signed.
|
||||
NOTARIZATION_ARTIFACT="$BUNDLE_DIR/${WARP_BIN}_notarize.zip"
|
||||
if [[ -e "$NOTARIZATION_ARTIFACT" ]]; then
|
||||
echo "Removing old notarization artifact..."
|
||||
rm "$NOTARIZATION_ARTIFACT"
|
||||
fi
|
||||
|
||||
# Create a .zip archive to notarize.
|
||||
ditto -c -k "$OUT_DIR/$WARP_BIN" "$NOTARIZATION_ARTIFACT"
|
||||
|
||||
# It's not possible to staple notarization tickets to standalone binaries:
|
||||
# https://developer.apple.com/documentation/security/customizing-the-notarization-workflow?language=objc#Staple-the-ticket-to-your-distribution
|
||||
STAPLE_TICKET=false
|
||||
fi
|
||||
fi
|
||||
|
||||
########################
|
||||
## Step 4: Create DMG ##
|
||||
########################
|
||||
|
||||
if [[ "$ARTIFACT" = app ]]; then
|
||||
function create_warp_dmg() {
|
||||
echo "Creating $DMG_DIR/$DMG_NAME..."
|
||||
rm "$DMG_DIR/$DMG_NAME" || true
|
||||
local source_folder="$1"
|
||||
|
||||
local args=(
|
||||
--volname Warp
|
||||
# For --no-internet-enable, see https://github.com/create-dmg/create-dmg/issues/179
|
||||
--no-internet-enable
|
||||
--background app/assets/resources/mac/warp_install_image.png
|
||||
--icon-size 128
|
||||
--window-size 700 500
|
||||
--format UDZO
|
||||
--app-drop-link 550 250
|
||||
--icon "$WARP_APP_NAME.app" 150 250
|
||||
# macOS 26.4 Beta has issues with mounting HFS+ DMGs, so we're using APFS instead.
|
||||
# APFS has been supported as a DMG filesystem since macOS 10.13, and we target 10.14
|
||||
# as our minimum version.
|
||||
#
|
||||
# See: https://developer.apple.com/documentation/macos-release-notes/macos-26_4-release-notes#External-Media
|
||||
--filesystem APFS
|
||||
)
|
||||
|
||||
# Skip running an AppleScript to format the DMG contents if running in Namespace - this consistently times out.
|
||||
# See https://github.com/create-dmg/create-dmg/issues/72
|
||||
if [[ "${RUNNER_NAME:-}" == nsc-* ]]; then
|
||||
args+=(--skip-jenkins)
|
||||
fi
|
||||
|
||||
args+=("$DMG_DIR/$DMG_NAME" "$source_folder")
|
||||
|
||||
create-dmg "${args[@]}"
|
||||
}
|
||||
|
||||
# If we're codesigning, stage the signed app and use that to create the DMG.
|
||||
# Otherwise, create a DMG from the bundle dir directly.
|
||||
if [[ $CODESIGN = true ]]; then
|
||||
if test -d "$DMG_DIR"; then
|
||||
echo "Clearing old dmg directory $DMG_DIR"
|
||||
rm -r "$DMG_DIR"
|
||||
fi
|
||||
echo "Creating $DMG_DIR"
|
||||
mkdir -p "$DMG_DIR"
|
||||
cp -R "$BUNDLE_DIR/$WARP_APP_NAME.app" "$DMG_DIR"
|
||||
|
||||
create_warp_dmg "$DMG_DIR"
|
||||
|
||||
echo "Codesigning $DMG_DIR/$DMG_NAME..."
|
||||
codesign -s "$APPLE_TEAM_ID" --timestamp "$DMG_DIR/$DMG_NAME"
|
||||
|
||||
NOTARIZATION_ARTIFACT="$DMG_DIR/$DMG_NAME"
|
||||
STAPLE_TICKET=true
|
||||
else
|
||||
echo "Creating $DMG_DIR"
|
||||
mkdir -p "$DMG_DIR"
|
||||
|
||||
echo "Cleaning up any existing DMG files before creating new ones..."
|
||||
cleanup_dmg_files "$DMG_DIR"
|
||||
|
||||
create_warp_dmg "$BUNDLE_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
##############################
|
||||
## Step 5: Notarize the app ##
|
||||
##############################
|
||||
|
||||
if [[ $CODESIGN = true ]]; then
|
||||
echo "Uploading $NOTARIZATION_ARTIFACT to Apple for notarization..."
|
||||
xcrun notarytool submit "$NOTARIZATION_ARTIFACT" --apple-id "$WARP_NOTARIZATION_APPLE_ID" --password "$WARP_NOTARIZATION_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait
|
||||
|
||||
if [[ $STAPLE_TICKET = true ]]; then
|
||||
echo "Attempting to staple the notarization ticket to $NOTARIZATION_ARTIFACT..."
|
||||
xcrun stapler staple "$NOTARIZATION_ARTIFACT"
|
||||
fi
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Notarization failed; see above output for details."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the notarization results (this is format-dependent)
|
||||
echo "Verifying notarization ticket..."
|
||||
if [[ "$ARTIFACT" = app ]]; then
|
||||
xcrun stapler validate "$DMG_DIR/$DMG_NAME"
|
||||
elif [[ "$ARTIFACT" = cli ]]; then
|
||||
spctl -a -t open --context context:primary-signature -vv "$OUT_DIR/$WARP_BIN"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
#######################################
|
||||
## Step 6: Copy and output artifacts ##
|
||||
#######################################
|
||||
|
||||
if [[ "$ARTIFACT" = app ]]; then
|
||||
echo "Copying dmg and app to $OUT_DIR"
|
||||
cp -R "$BUNDLE_DIR/$WARP_APP_NAME.app" "$OUT_DIR"
|
||||
cp "$DMG_DIR/$DMG_NAME" "$OUT_DIR/$FINAL_DMG_NAME"
|
||||
fi
|
||||
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# location of the artifacts so they can be referenced by subsequent actions.
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
echo "::echo::on"
|
||||
|
||||
# For individual architecture builds, output binary information
|
||||
if [[ -n "$TARGET_ARCH" ]]; then
|
||||
echo "binary_path=$BINARY_PATH" >> "$GITHUB_OUTPUT"
|
||||
echo "target_arch=$TARGET_ARCH" >> "$GITHUB_OUTPUT"
|
||||
echo "rust_target=$DEFAULT_TARGET" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# If the dSYM is available, output both its path and its realpath.
|
||||
# The realpath is needed because the dSYM is a symlink, and when preserving
|
||||
# the build artifacts, we want to preserve both the symlink and its target.
|
||||
DSYM_PATH="${BINARY_PATH}.dSYM"
|
||||
if [[ -d "$DSYM_PATH" ]]; then
|
||||
echo "dsym_path=$DSYM_PATH" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# If the dSYM is a symlink, output its real path.
|
||||
if [[ -L "$DSYM_PATH" ]]; then
|
||||
echo "dsym_realpath=$(relpath $(realpath $DSYM_PATH))" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "dock_tile_plugin_dir=$DOCK_TILE_PLUGIN_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "frameworks_dir=app/frameworks/${FRAMEWORK_OVERRIDE:-default}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# For full bundles, output DMG information
|
||||
if [[ -f "$OUT_DIR/$FINAL_DMG_NAME" ]]; then
|
||||
echo "dmg_name=$FINAL_DMG_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "dmg_path=$DMG_PATH" >> "$GITHUB_OUTPUT"
|
||||
echo "dsym_folder_path=$OUT_DIR" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
if [[ -n "$BUNDLED_RESOURCES_DIR" ]]; then
|
||||
echo "bundled_resources_dir=$BUNDLED_RESOURCES_DIR" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
echo "::echo::off"
|
||||
fi
|
||||
|
||||
if [[ $OPEN_AFTER_BUNDLE = true ]]; then
|
||||
open "$OUT_DIR"
|
||||
fi
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Installs macOS-specific build dependencies required to build Warp.
|
||||
|
||||
xcodebuild -downloadComponent MetalToolchain
|
||||
Executable
+142
@@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# macOS-specific implementation of `./script/run`. Runs a local version of
|
||||
# Warp as a real 'app' instead of a bare executable, so macOS can treat it
|
||||
# like a real signed app (custom URL schemes, user notifications, etc.).
|
||||
#
|
||||
# This script is invoked by `./script/run`, which handles cross-platform
|
||||
# setup (install_channel_config, binary name detection, feature-to-env-var
|
||||
# mapping). The following env vars are expected to be set by the caller:
|
||||
# WARP_BIN_NAME — "warp" (internal local build) or "warp-oss"
|
||||
# WARP_CHANNEL — "local" or "oss"
|
||||
# FEATURES — comma-separated cargo features (already normalized)
|
||||
# Must be called from the root directory of the warp repo.
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
: "${WARP_BIN_NAME:?WARP_BIN_NAME must be set (invoke via ./script/run)}"
|
||||
: "${WARP_CHANNEL:?WARP_CHANNEL must be set (invoke via ./script/run)}"
|
||||
: "${FEATURES:?FEATURES must be set (invoke via ./script/run)}"
|
||||
|
||||
if [ "$WARP_CHANNEL" = "local" ]; then
|
||||
WARP_APP_PATH="target/debug/bundle/osx/WarpLocal.app"
|
||||
WARP_SCHEME_NAME="warplocal"
|
||||
else
|
||||
WARP_APP_PATH="target/debug/bundle/osx/WarpOss.app"
|
||||
WARP_SCHEME_NAME="warposs"
|
||||
fi
|
||||
DONT_OPEN=false
|
||||
# Launches the binary with "open", meaning the Warp process is
|
||||
# launched by the MacOS application launcher instead of a shell session.
|
||||
# Note that since Warp isn't launched as a child process, using ctrl+c
|
||||
# won't kill the app (but will kill the tail process displaying output).
|
||||
# tl;dr better simulates running Warp Dev/Stable
|
||||
OPEN_WITH_LAUNCHD=false
|
||||
|
||||
# Arguments to pass directly Warp (specified after --)
|
||||
# This is not supported when opening with launchd, as passing CLI arguments to
|
||||
# an application doesn't make sense.
|
||||
WARP_ARGS=()
|
||||
|
||||
# Export this variable so it can be referenced by app/build.rs later when
|
||||
# running `cargo bundle`.
|
||||
export FRAMEWORK_OVERRIDE="dev"
|
||||
|
||||
PARAMS=""
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--)
|
||||
shift
|
||||
WARP_ARGS=("$@")
|
||||
break
|
||||
;;
|
||||
--dont-open)
|
||||
DONT_OPEN=true
|
||||
shift
|
||||
;;
|
||||
--release)
|
||||
echo "Detected release build, pointing at release bundle under target/release/bundle"
|
||||
if [ "$WARP_CHANNEL" = "local" ]; then
|
||||
WARP_APP_PATH="target/release/bundle/osx/WarpLocal.app"
|
||||
else
|
||||
WARP_APP_PATH="target/release/bundle/osx/WarpOss.app"
|
||||
fi
|
||||
PARAMS="$PARAMS $1"
|
||||
shift
|
||||
;;
|
||||
--profile)
|
||||
PROFILE="$2"
|
||||
shift 2
|
||||
if [ "$WARP_CHANNEL" = "local" ]; then
|
||||
WARP_APP_PATH="target/${PROFILE}/bundle/osx/WarpLocal.app"
|
||||
else
|
||||
WARP_APP_PATH="target/${PROFILE}/bundle/osx/WarpOss.app"
|
||||
fi
|
||||
PARAMS="$PARAMS --profile $PROFILE"
|
||||
;;
|
||||
--open_with_launchd)
|
||||
OPEN_WITH_LAUNCHD=true
|
||||
shift
|
||||
;;
|
||||
--generate-schema)
|
||||
GENERATE_SCHEMA=true
|
||||
shift
|
||||
;;
|
||||
*) # preserve positional arguments
|
||||
PARAMS="$PARAMS $1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
rm -rf "$WARP_APP_PATH"
|
||||
|
||||
pushd app > /dev/null
|
||||
echo "Bundling app (bin: $WARP_BIN_NAME)..."
|
||||
cargo bundle --bin "$WARP_BIN_NAME" --features "$FEATURES" $PARAMS
|
||||
echo "Successfully bundled..."
|
||||
popd > /dev/null
|
||||
|
||||
if [[ ",$FEATURES," =~ ",cocoa_sentry," ]]; then
|
||||
echo "Copying Sentry framework into app bundle..."
|
||||
SENTRY_FRAMEWORK="app/frameworks/dev/Sentry-Dynamic-WithARM64e.xcframework/macos-arm64_arm64e_x86_64/Sentry.framework"
|
||||
cp -a "$SENTRY_FRAMEWORK" "$WARP_APP_PATH/Contents/Frameworks/"
|
||||
fi
|
||||
echo "Adding rpath to support mac frameworks (e.g. Sentry)"
|
||||
install_name_tool -add_rpath "@executable_path/../Frameworks" "$WARP_APP_PATH/Contents/MacOS/$WARP_BIN_NAME"
|
||||
|
||||
export WARP_SCHEME_NAME
|
||||
export WARP_PLIST_PATH="$WARP_APP_PATH/Contents/Info.plist"
|
||||
./script/update_plist
|
||||
|
||||
echo "Preparing bundled resources..."
|
||||
if [ "${GENERATE_SCHEMA:-false}" != "true" ]; then
|
||||
export SKIP_SETTINGS_SCHEMA=1
|
||||
fi
|
||||
NO_LICENSES=1 "${REPO_ROOT}/script/prepare_bundled_resources" "$WARP_APP_PATH/Contents/Resources" "$WARP_CHANNEL"
|
||||
|
||||
"${REPO_ROOT}/script/compile_icon" "$WARP_CHANNEL" "$WARP_APP_PATH"
|
||||
|
||||
if [[ ",$FEATURES," =~ ",heap_usage_tracking," ]]; then
|
||||
echo "Bundling pprof..."
|
||||
HELPERS_DIR="$WARP_APP_PATH/Contents/Helpers"
|
||||
"${REPO_ROOT}/script/prepare_bundled_pprof" "$HELPERS_DIR"
|
||||
fi
|
||||
|
||||
echo "Codesigning app..."
|
||||
SIGNING_CERT="$(security find-identity -p codesigning -v | grep "Apple Development" | awk '{print $2}' | head -1)"
|
||||
codesign --force --deep --options runtime --sign "${SIGNING_CERT:--}" "$WARP_APP_PATH" --entitlements script/Debug-Entitlements.plist
|
||||
|
||||
if [ "$DONT_OPEN" = false ] ; then
|
||||
if [ "$OPEN_WITH_LAUNCHD" = true ]; then
|
||||
echo "Launching with MacOS application launcher"
|
||||
PATH="" /usr/bin/open "./$WARP_APP_PATH"
|
||||
tail -f ~/Library/Logs/warp_local.log
|
||||
else
|
||||
echo "Opening app at ./$WARP_APP_PATH/Contents/MacOS/$WARP_BIN_NAME"
|
||||
"./$WARP_APP_PATH/Contents/MacOS/$WARP_BIN_NAME" "${WARP_ARGS[@]}"
|
||||
fi
|
||||
fi
|
||||
Executable
+237
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Clean up zip files on exit.
|
||||
cleanup() {
|
||||
if [[ -n "${zip_file:-}" ]] && [[ -f "$zip_file" ]]; then
|
||||
rm -f "$zip_file"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Parse arguments.
|
||||
DIR=""
|
||||
VERSION=""
|
||||
STATIC=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dir)
|
||||
DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--version)
|
||||
VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--static)
|
||||
STATIC=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1"
|
||||
echo "Usage: $0 --dir <frameworks_dir> --version <version> [--static]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$DIR" ]] || [[ -z "$VERSION" ]]; then
|
||||
echo "Error: Both --dir and --version are required"
|
||||
echo "Usage: $0 --dir <frameworks_dir> --version <version> [--static]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure DIR is absolute or relative to current directory.
|
||||
if [[ ! "$DIR" = /* ]]; then
|
||||
DIR="$(pwd)/$DIR"
|
||||
fi
|
||||
|
||||
# Determine framework type.
|
||||
if [[ "$STATIC" == "true" ]]; then
|
||||
FRAMEWORK_TYPE="static"
|
||||
XCFRAMEWORK_NAME="Sentry.xcframework"
|
||||
else
|
||||
FRAMEWORK_TYPE="dynamic"
|
||||
XCFRAMEWORK_NAME="Sentry-Dynamic-WithARM64e.xcframework"
|
||||
fi
|
||||
|
||||
# Framework path.
|
||||
FRAMEWORK_PATH="$DIR/$XCFRAMEWORK_NAME/macos-arm64_arm64e_x86_64/Sentry.framework"
|
||||
|
||||
# Function to get framework version from Info.plist.
|
||||
get_framework_version() {
|
||||
local framework_path="$1"
|
||||
local plist_path="$framework_path/Resources/Info.plist"
|
||||
|
||||
if [[ ! -f "$plist_path" ]]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$plist_path" 2>/dev/null || echo ""
|
||||
}
|
||||
|
||||
# Fetch a URL with retries. Writes the response body to stdout.
|
||||
# Usage: fetch_with_retries <url> [curl_args...]
|
||||
fetch_with_retries() {
|
||||
local url="$1"
|
||||
shift
|
||||
local max_retries=3
|
||||
local attempt=1
|
||||
local wait_seconds=2
|
||||
|
||||
while (( attempt <= max_retries )); do
|
||||
local http_code
|
||||
local response
|
||||
|
||||
# Fetch the URL, capturing both the response body and the HTTP status code.
|
||||
# --fail-with-body ensures we still get the response body on HTTP errors.
|
||||
response=$(curl --fail-with-body --show-error --write-out "\n%{http_code}" "$@" "$url") || true
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" =~ ^2 ]]; then
|
||||
echo "$response"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Only retry on transient errors (rate limiting or server errors).
|
||||
local is_retryable=false
|
||||
if [[ "$http_code" == "429" || "$http_code" =~ ^5 ]]; then
|
||||
is_retryable=true
|
||||
fi
|
||||
|
||||
echo "[attempt $attempt/$max_retries] HTTP $http_code from $url" >&2
|
||||
if [[ "$is_retryable" == "true" ]] && (( attempt < max_retries )); then
|
||||
echo "Retrying in ${wait_seconds}s..." >&2
|
||||
sleep "$wait_seconds"
|
||||
wait_seconds=$(( wait_seconds * 2 ))
|
||||
attempt=$(( attempt + 1 ))
|
||||
else
|
||||
if [[ "$is_retryable" != "true" ]]; then
|
||||
echo "Non-retryable HTTP status; giving up." >&2
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Error: Failed to fetch $url (HTTP $http_code). Response:" >&2
|
||||
echo "$response" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to download and extract framework.
|
||||
download_framework() {
|
||||
local xcframework_name="$1"
|
||||
local xcframework_dir="$DIR/$xcframework_name"
|
||||
local download_url="https://github.com/getsentry/sentry-cocoa/releases/download/${VERSION}/${xcframework_name}.zip"
|
||||
zip_file="$DIR/${xcframework_name}.zip"
|
||||
|
||||
echo "Downloading $xcframework_name version $VERSION..."
|
||||
|
||||
# Create directory if it doesn't exist.
|
||||
mkdir -p "$DIR"
|
||||
|
||||
# Require jq for SHA256 verification.
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is required for SHA256 verification"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fetch release metadata from GitHub API.
|
||||
# Prefer `gh api` for authenticated requests (avoids GitHub rate limits in CI).
|
||||
local api_url="https://api.github.com/repos/getsentry/sentry-cocoa/releases/tags/${VERSION}"
|
||||
echo "Fetching release metadata from $api_url"
|
||||
local api_response
|
||||
if command -v gh &> /dev/null; then
|
||||
if ! api_response=$(gh api "repos/getsentry/sentry-cocoa/releases/tags/${VERSION}"); then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! api_response=$(fetch_with_retries "$api_url" --silent); then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate that the response contains an assets array before trying to iterate it.
|
||||
local assets_type
|
||||
assets_type=$(echo "$api_response" | jq -r '.assets | type')
|
||||
if [[ "$assets_type" != "array" ]]; then
|
||||
echo "Error: GitHub API response does not contain an assets array (got: $assets_type)." >&2
|
||||
echo "Raw API response:" >&2
|
||||
echo "$api_response" | head -50 >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local expected_sha256
|
||||
expected_sha256=$(echo "$api_response" | jq -r ".assets[] | select(.name == \"$xcframework_name.zip\") | .digest // empty" | cut -d: -f2)
|
||||
|
||||
if [[ -z "$expected_sha256" ]]; then
|
||||
echo "Error: Could not find SHA256 checksum for $xcframework_name.zip in release $VERSION."
|
||||
echo "Available assets:"
|
||||
echo "$api_response" | jq -r '.assets[].name'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Expected SHA256: $expected_sha256"
|
||||
|
||||
# Download the framework. Prefer `gh release download` for authentication.
|
||||
echo "Downloading from $download_url"
|
||||
if command -v gh &> /dev/null; then
|
||||
if ! gh release download "${VERSION}" --repo getsentry/sentry-cocoa --pattern "${xcframework_name}.zip" --dir "$DIR" --clobber; then
|
||||
echo "Error: Failed to download $xcframework_name via gh CLI"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! curl -L -f --show-error -o "$zip_file" "$download_url"; then
|
||||
echo "Error: Failed to download $xcframework_name from $download_url"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify checksum if we have one.
|
||||
if [[ -n "$expected_sha256" ]]; then
|
||||
echo "Verifying checksum..."
|
||||
local actual_sha256
|
||||
actual_sha256=$(shasum -a 256 "$zip_file" | awk '{print $1}')
|
||||
|
||||
if [[ "$actual_sha256" != "$expected_sha256" ]]; then
|
||||
echo "Error: SHA256 checksum mismatch!"
|
||||
echo " Expected: $expected_sha256"
|
||||
echo " Actual: $actual_sha256"
|
||||
exit 1
|
||||
fi
|
||||
echo "Checksum verified successfully"
|
||||
fi
|
||||
|
||||
# Remove existing framework if present.
|
||||
if [[ -d "$xcframework_dir" ]]; then
|
||||
rm -rf "$xcframework_dir"
|
||||
fi
|
||||
|
||||
# Extract only the macos-arm64_arm64e_x86_64 subdirectory.
|
||||
echo "Extracting $xcframework_name (macOS frameworks only)..."
|
||||
if ! unzip -q "$zip_file" "${xcframework_name}/macos-arm64_arm64e_x86_64/*" -d "$DIR"; then
|
||||
echo "Error: Failed to extract framework from zip. Expected path: ${xcframework_name}/macos-arm64_arm64e_x86_64/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Successfully installed $xcframework_name version $VERSION"
|
||||
}
|
||||
|
||||
# Check and download the needed framework.
|
||||
CURRENT_VERSION=$(get_framework_version "$FRAMEWORK_PATH")
|
||||
if [[ -z "$CURRENT_VERSION" ]] || [[ "$CURRENT_VERSION" != "$VERSION" ]]; then
|
||||
if [[ -z "$CURRENT_VERSION" ]]; then
|
||||
echo "$FRAMEWORK_TYPE framework not found or missing Info.plist"
|
||||
else
|
||||
echo "$FRAMEWORK_TYPE framework version mismatch: found $CURRENT_VERSION, expected $VERSION"
|
||||
fi
|
||||
download_framework "$XCFRAMEWORK_NAME"
|
||||
else
|
||||
echo "$FRAMEWORK_TYPE framework version $VERSION already installed"
|
||||
fi
|
||||
|
||||
echo "Framework up to date"
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env -S fontforge -script
|
||||
# Script that patches a given font to include the Warp logo as a glyph in the font,
|
||||
# using a unicode codepoint in the private use area.
|
||||
import os
|
||||
import sys
|
||||
|
||||
import fontforge
|
||||
import psMat
|
||||
|
||||
# Default values match what we used when initially patching Roboto.
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
DEFAULT_SVG_PATH = os.path.join(SCRIPT_DIR, "warp.svg")
|
||||
DEFAULT_CODEPOINT = 0xE500 # Private Use Area
|
||||
DEFAULT_GLYPH_NAME = "warpLogo"
|
||||
|
||||
|
||||
def usage(exit_code: int) -> None:
|
||||
prog = os.path.basename(sys.argv[0])
|
||||
print(
|
||||
"\n".join(
|
||||
[
|
||||
f"Usage: {prog} <font.ttf> [--svg <icon.svg>] [--codepoint <hex>] [--name <glyph_name>]",
|
||||
"",
|
||||
"Patches the given font by importing an SVG icon as a new glyph.",
|
||||
"Defaults:",
|
||||
f" --svg {DEFAULT_SVG_PATH}",
|
||||
f" --codepoint 0x{DEFAULT_CODEPOINT:04X}",
|
||||
f" --name {DEFAULT_GLYPH_NAME}",
|
||||
]
|
||||
)
|
||||
)
|
||||
raise SystemExit(exit_code)
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
if len(argv) < 2 or argv[1] in ("-h", "--help"):
|
||||
usage(0 if len(argv) >= 2 else 2)
|
||||
|
||||
font_path = argv[1]
|
||||
svg_path = DEFAULT_SVG_PATH
|
||||
codepoint = DEFAULT_CODEPOINT
|
||||
glyph_name = DEFAULT_GLYPH_NAME
|
||||
|
||||
i = 2
|
||||
while i < len(argv):
|
||||
arg = argv[i]
|
||||
|
||||
if arg == "--svg":
|
||||
i += 1
|
||||
if i >= len(argv):
|
||||
usage(2)
|
||||
svg_path = argv[i]
|
||||
elif arg == "--codepoint":
|
||||
i += 1
|
||||
if i >= len(argv):
|
||||
usage(2)
|
||||
v = argv[i]
|
||||
# Accept 0xE500, E500, or decimal.
|
||||
codepoint = int(v, 16) if (v.startswith("0x") or any(c in v.lower() for c in "abcdef")) else int(v)
|
||||
elif arg == "--name":
|
||||
i += 1
|
||||
if i >= len(argv):
|
||||
usage(2)
|
||||
glyph_name = argv[i]
|
||||
else:
|
||||
print(f"Unknown argument: {arg}")
|
||||
usage(2)
|
||||
|
||||
i += 1
|
||||
|
||||
return font_path, svg_path, codepoint, glyph_name
|
||||
|
||||
|
||||
def patch_font(font_path: str, svg_path: str, codepoint: int, glyph_name: str) -> None:
|
||||
if not os.path.exists(font_path):
|
||||
raise SystemExit(f"Font not found: {font_path}")
|
||||
|
||||
if not os.path.exists(svg_path):
|
||||
raise SystemExit(f"SVG not found: {svg_path}")
|
||||
|
||||
font = fontforge.open(font_path)
|
||||
|
||||
# Create or replace the glyph.
|
||||
g = font.createChar(codepoint, glyph_name)
|
||||
g.clear()
|
||||
|
||||
# Import the outlines.
|
||||
g.importOutlines(svg_path)
|
||||
|
||||
# Clean up and make it robust for rasterization.
|
||||
g.removeOverlap()
|
||||
g.simplify()
|
||||
g.correctDirection()
|
||||
|
||||
# Keep the logo from looking low: align the bottom of the imported outline to the baseline.
|
||||
# (This is the same adjustment we ended up applying after the initial patch.)
|
||||
xmin, ymin, xmax, ymax = g.boundingBox()
|
||||
if ymin != 0:
|
||||
g.transform(psMat.translate(0, -ymin))
|
||||
g.round()
|
||||
|
||||
font.generate(font_path)
|
||||
font.close()
|
||||
|
||||
|
||||
def main():
|
||||
font_path, svg_path, codepoint, glyph_name = parse_args(sys.argv)
|
||||
patch_font(font_path, svg_path, codepoint, glyph_name)
|
||||
print(f"Patched {font_path} with U+{codepoint:04X} ({glyph_name})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Prepares bundled pprof for distribution.
|
||||
#
|
||||
# This script copies a pprof binary that should be bundled with Warp into a
|
||||
# destination directory.
|
||||
#
|
||||
# Usage:
|
||||
# prepare_bundled_pprof <destination_directory>
|
||||
#
|
||||
# Arguments:
|
||||
# destination_directory: The directory where pprof should be installed.
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Error: Expected 1 argument (destination directory) but received $#" >&2
|
||||
echo "Usage: $0 <destination_directory>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEST_DIR="$1"
|
||||
|
||||
# Create the destination directory if it doesn't exist
|
||||
mkdir -p "$DEST_DIR"
|
||||
|
||||
# Copy pprof
|
||||
echo "Copying pprof to $DEST_DIR/pprof"
|
||||
rm -f "$DEST_DIR/pprof"
|
||||
cp "$(go tool -n pprof)" "$DEST_DIR/pprof"
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Prepares bundled resources for distribution.
|
||||
#
|
||||
# This script copies resources that should be bundled with Warp into a
|
||||
# destination directory. It is used by macOS and Linux build scripts.
|
||||
#
|
||||
# Usage:
|
||||
# prepare_bundled_resources <destination_directory> [channel] [cargo_profile]
|
||||
#
|
||||
# Arguments:
|
||||
# destination_directory: The directory where resources should be installed.
|
||||
# Resources will be copied to subdirectories within
|
||||
# this path (e.g., $DEST_DIR/skills).
|
||||
# channel: (Optional) Release channel (local, dev, preview,
|
||||
# stable). Used to include channel-gated skills.
|
||||
# cargo_profile: (Optional) Cargo build profile to use when
|
||||
# generating the settings schema. Reusing the same
|
||||
# profile as the main build avoids recompiling deps.
|
||||
#
|
||||
# Environment variables:
|
||||
# SKIP_SETTINGS_SCHEMA: Set to "1" to skip generating the JSON settings
|
||||
# schema. By default the schema is always generated
|
||||
# so that production bundles never accidentally omit it.
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -lt 1 ] || [ $# -gt 3 ]; then
|
||||
echo "Error: Expected 1-3 arguments but received $#" >&2
|
||||
echo "Usage: $0 <destination_directory> [channel] [cargo_profile]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEST_DIR="$1"
|
||||
CHANNEL="${2:-}"
|
||||
CARGO_PROFILE="${3:-}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
RESOURCES_SRC="$REPO_ROOT/resources"
|
||||
|
||||
# Validate that the source resources directory exists
|
||||
if [ ! -d "$RESOURCES_SRC" ]; then
|
||||
echo "Error: Resources directory not found at $RESOURCES_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create the destination directory if it doesn't exist
|
||||
mkdir -p "$DEST_DIR"
|
||||
|
||||
# Copy bundled resources
|
||||
if [ -d "$RESOURCES_SRC/bundled" ]; then
|
||||
echo "Copying bundled resources to $DEST_DIR/bundled"
|
||||
rm -rf "$DEST_DIR/bundled"
|
||||
cp -R "$RESOURCES_SRC/bundled" "$DEST_DIR/bundled"
|
||||
else
|
||||
echo "Warning: No bundled directory found at $RESOURCES_SRC/bundled" >&2
|
||||
fi
|
||||
|
||||
if [ -n "$GIT_RELEASE_TAG" ]; then
|
||||
VERSION_METADATA_DIR="$DEST_DIR/bundled/metadata"
|
||||
VERSION_METADATA_PATH="$VERSION_METADATA_DIR/version.json"
|
||||
|
||||
echo "Writing bundled Warp version metadata to $VERSION_METADATA_PATH"
|
||||
mkdir -p "$VERSION_METADATA_DIR"
|
||||
printf '{\n "warp_version": "%s"\n}\n' "$GIT_RELEASE_TAG" > "$VERSION_METADATA_PATH"
|
||||
fi
|
||||
|
||||
# Copy channel-gated skills matching the current release channel.
|
||||
GATED_SRC="$REPO_ROOT/resources/channel-gated-skills"
|
||||
DEST_SKILLS="$DEST_DIR/bundled/skills"
|
||||
|
||||
if [ -n "$CHANNEL" ] && [ -d "$GATED_SRC" ]; then
|
||||
echo "Copying channel-gated skills for channel '$CHANNEL'..."
|
||||
"$SCRIPT_DIR/copy_conditional_skills" \
|
||||
"$CHANNEL" \
|
||||
"$GATED_SRC" \
|
||||
"$DEST_SKILLS"
|
||||
fi
|
||||
|
||||
# Generate third-party license attribution.
|
||||
#
|
||||
# Additional (non-Cargo) third-party license files to include in the output.
|
||||
# Each entry is: "Component Name|License Identifier|path/to/LICENSE/relative/to/repo/root"
|
||||
# When adding a new third-party component to the bundle, add its license file
|
||||
# to the repo alongside the component and add an entry here.
|
||||
ADDITIONAL_LICENSES=(
|
||||
"Hack Font|MIT|app/assets/bundled/fonts/hack/LICENSE.md"
|
||||
"Roboto Font|SIL Open Font License|app/assets/bundled/fonts/roboto/LICENSE.txt"
|
||||
"bash-preexec|MIT|app/assets/bundled/bootstrap/bash-preexec-LICENSE.md"
|
||||
"Claude API Skill|Apache-2.0|resources/bundled/skills/claude-api/LICENSE.txt"
|
||||
"rudder-sdk-rust|MIT|app/src/server/telemetry/LICENSE-RUDDER-SDK-RUST.txt"
|
||||
"Windows Terminal|MIT|app/assets/windows/LICENSE-WINDOWS-TERMINAL"
|
||||
"GitHub Desktop|MIT|app/src/code_review/GITHUB-DESKTOP-LICENSE"
|
||||
)
|
||||
|
||||
# Build the third-party licenses file unless the NO_LICENSES envvar is set.
|
||||
if [ -z "$NO_LICENSES" ]; then
|
||||
LICENSES_OUTPUT="$DEST_DIR/THIRD_PARTY_LICENSES.txt"
|
||||
echo "Generating third-party licenses at $LICENSES_OUTPUT"
|
||||
cargo about generate \
|
||||
--workspace \
|
||||
--manifest-path "$REPO_ROOT/Cargo.toml" \
|
||||
-c "$REPO_ROOT/about.toml" \
|
||||
-o "$LICENSES_OUTPUT" \
|
||||
"$REPO_ROOT/about.hbs"
|
||||
|
||||
# Append additional (non-Cargo) third-party licenses.
|
||||
for entry in "${ADDITIONAL_LICENSES[@]}"; do
|
||||
IFS='|' read -r name license_id license_path <<< "$entry"
|
||||
license_file="$REPO_ROOT/$license_path"
|
||||
if [ ! -f "$license_file" ]; then
|
||||
echo "Error: License file not found: $license_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '\n%s (%s)\n' "$name" "$license_id" >> "$LICENSES_OUTPUT"
|
||||
printf '%0.s-' {1..80} >> "$LICENSES_OUTPUT"
|
||||
printf '\n' >> "$LICENSES_OUTPUT"
|
||||
cat "$license_file" >> "$LICENSES_OUTPUT"
|
||||
printf '\n' >> "$LICENSES_OUTPUT"
|
||||
done
|
||||
fi
|
||||
|
||||
# Generate settings JSON schema unless explicitly skipped.
|
||||
if [ "${SKIP_SETTINGS_SCHEMA:-}" != "1" ]; then
|
||||
SCHEMA_OUTPUT="$DEST_DIR/settings_schema.json"
|
||||
echo "Generating settings schema at $SCHEMA_OUTPUT"
|
||||
|
||||
SCHEMA_CMD=(cargo run)
|
||||
if [ -n "$CARGO_PROFILE" ]; then
|
||||
SCHEMA_CMD+=(--profile "$CARGO_PROFILE")
|
||||
fi
|
||||
SCHEMA_CMD+=(--manifest-path "$REPO_ROOT/Cargo.toml" --bin generate_settings_schema --)
|
||||
if [ -n "$CHANNEL" ]; then
|
||||
SCHEMA_CMD+=(--channel "$CHANNEL")
|
||||
fi
|
||||
SCHEMA_CMD+=("$SCHEMA_OUTPUT")
|
||||
|
||||
"${SCHEMA_CMD[@]}"
|
||||
fi
|
||||
|
||||
echo "Successfully prepared bundled resources in $DEST_DIR"
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to run presubmit checks. Devs can run this script locally before sending out PRs for review to ensure
|
||||
# CI is passing for their PR.
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
FMT_COMMAND="cargo fmt"
|
||||
echo "Running $FMT_COMMAND..."
|
||||
set -e
|
||||
EXIT_CODE=0
|
||||
$FMT_COMMAND -- --check || EXIT_CODE=$?
|
||||
if [[ $EXIT_CODE -ne 0 ]]; then
|
||||
echo 'Run `'"$FMT_COMMAND"'` to fix.'
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
|
||||
echo "Cargo fmt succeeded..."
|
||||
|
||||
echo "Running clippy..."
|
||||
# Exclude warp_completer because we run clippy on it with default features (rather than all features) below.
|
||||
cargo clippy --workspace --exclude warp_completer --all-targets --all-features --tests -- -D warnings
|
||||
# Run clippy on warp_completer with default, rather than all features enabled, because there is
|
||||
# feature-gated logic for the WIP completions-on-js implementation.
|
||||
cargo clippy -p warp_completer --all-targets --tests -- -D warnings
|
||||
echo "clippy succeeded..."
|
||||
|
||||
echo "Running clang-format..."
|
||||
./script/run-clang-format.py -r --extensions 'c,h,cpp,m' ./crates/warpui/src/ ./app/src/
|
||||
echo "clang-format succeeded..."
|
||||
|
||||
echo "Running wgslfmt..."
|
||||
# Normally a recursive glob pattern will do the trick, e.g. **/*.wgsl
|
||||
# However, the default bash version in MacOS is too old to support that, so we use `find` instead.
|
||||
find . -name "*.wgsl" -exec wgslfmt --check {} +
|
||||
echo "wgslfmt succeeded..."
|
||||
|
||||
# check to see if we can run powershell on the device
|
||||
if command -v pwsh /dev/null 2>&1; then
|
||||
echo "Running PSScriptAnalyzer..."
|
||||
./script/lint_powershell -ci
|
||||
echo "PsScriptAnalyzer succeeded..."
|
||||
elif [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
# If we are in CI, and fail to find powershell, we should automatically fail
|
||||
echo "No powershell installation detected! Aborting!"
|
||||
exit 1
|
||||
else
|
||||
# Otherwise, post a notice that we are skipping powershell
|
||||
echo "No Powershell detected! skipping PSScriptAnalyzer!"
|
||||
fi
|
||||
|
||||
echo "Running tests via nextest..."
|
||||
cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2
|
||||
# Run warp_completer tests with the "v2" (completions-on-js) flag enabled. We do this to ensure that
|
||||
# the v2 completions implementation doesn't regress/rot while it's development is paused.
|
||||
cargo nextest run -p warp_completer --features v2
|
||||
echo "Running doc tests..."
|
||||
cargo test --doc
|
||||
echo "Tests succeeded..."
|
||||
|
||||
echo "Congrats! All presubmits checks passed."
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Cross-platform entrypoint for running a local Warp build.
|
||||
#
|
||||
# On macOS this delegates to `./script/macos/run`, which builds and runs Warp
|
||||
# as a real `.app` bundle (with code signing, plist updates, etc.). On Linux
|
||||
# and Windows it invokes `cargo run` directly for the appropriate binary.
|
||||
#
|
||||
# This script owns all cross-platform setup (running install_channel_config,
|
||||
# detecting internal vs OSS builds, and mapping legacy `--features` entries
|
||||
# to environment variables), so platform-specific scripts can assume those
|
||||
# values are already provided via environment variables.
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
OS_TYPE="$(uname -s)"
|
||||
|
||||
FEATURES="gui"
|
||||
|
||||
./script/install_channel_config
|
||||
|
||||
# If warp_channel_config is on PATH, build the Local channel binary; otherwise build the OSS channel.
|
||||
if command -v warp-channel-config &>/dev/null; then
|
||||
WARP_BIN_NAME="warp"
|
||||
WARP_CHANNEL="local"
|
||||
else
|
||||
WARP_BIN_NAME="warp-oss"
|
||||
WARP_CHANNEL="oss"
|
||||
fi
|
||||
|
||||
# Shared argument parsing. macOS-specific flags (--dont-open,
|
||||
# --open_with_launchd, --generate-schema) are forwarded through MAC_ARGS and
|
||||
# silently ignored on other platforms.
|
||||
MAC_ARGS=()
|
||||
CARGO_PARAMS=()
|
||||
WARP_ARGS=()
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--)
|
||||
shift
|
||||
WARP_ARGS=("$@")
|
||||
break
|
||||
;;
|
||||
--features)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
FEATURES="$FEATURES,$2"
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--host-id)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
export WARP_CLOUD_MODE_DEFAULT_HOST="$2"
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--release)
|
||||
CARGO_PARAMS+=("$1")
|
||||
MAC_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
--profile)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
CARGO_PARAMS+=("$1" "$2")
|
||||
MAC_ARGS+=("$1" "$2")
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Argument for $1 is missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Unknown flags (including macOS-only flags like --dont-open) and any
|
||||
# positional arguments are forwarded to the platform-specific script.
|
||||
MAC_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# These cargo features were removed and replaced by environment variables read
|
||||
# by warp-channel-config. Intercept them here so that existing --features
|
||||
# invocations keep working.
|
||||
for mapping in \
|
||||
"with_local_server:WITH_LOCAL_SERVER" \
|
||||
"with_local_session_sharing_server:WITH_LOCAL_SESSION_SHARING_SERVER" \
|
||||
"with_sandbox_telemetry:WITH_SANDBOX_TELEMETRY"; do
|
||||
feature="${mapping%%:*}"
|
||||
env_var="${mapping##*:}"
|
||||
if [[ ",$FEATURES," =~ ,"$feature", ]]; then
|
||||
export "$env_var"=1
|
||||
FEATURES="$(echo "$FEATURES" | sed "s/,$feature,/,/; s/^$feature,//; s/,$feature$//; s/^$feature$//")"
|
||||
FEATURES="$(echo "$FEATURES" | sed 's/,,*/,/g; s/^,//; s/,$//')"
|
||||
echo "Note: '$feature' is no longer a cargo feature; setting $env_var=1 instead."
|
||||
fi
|
||||
done
|
||||
|
||||
export FEATURES
|
||||
export WARP_BIN_NAME
|
||||
export WARP_CHANNEL
|
||||
|
||||
if [[ "$OS_TYPE" = "Darwin" ]]; then
|
||||
if [[ ${#WARP_ARGS[@]} -gt 0 ]]; then
|
||||
exec ./script/macos/run "${MAC_ARGS[@]}" -- "${WARP_ARGS[@]}"
|
||||
else
|
||||
exec ./script/macos/run "${MAC_ARGS[@]}"
|
||||
fi
|
||||
elif [[ "$OS_TYPE" = "Linux" ]] || [[ "$OS_TYPE" =~ ^(MINGW64_NT|MSYS_NT) ]]; then
|
||||
echo "Running cargo run --bin $WARP_BIN_NAME --features \"$FEATURES\" ${CARGO_PARAMS[*]}"
|
||||
if [[ ${#WARP_ARGS[@]} -gt 0 ]]; then
|
||||
cargo run --bin "$WARP_BIN_NAME" --features "$FEATURES" "${CARGO_PARAMS[@]}" -- "${WARP_ARGS[@]}"
|
||||
else
|
||||
cargo run --bin "$WARP_BIN_NAME" --features "$FEATURES" "${CARGO_PARAMS[@]}"
|
||||
fi
|
||||
else
|
||||
echo "No run script defined for the current platform ($OS_TYPE)!" >&2
|
||||
exit 1
|
||||
fi
|
||||
Executable
+408
@@ -0,0 +1,408 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A wrapper script around clang-format, suitable for linting multiple files
|
||||
and to use for continuous integration.
|
||||
|
||||
This is an alternative API for the clang-format command line.
|
||||
It runs over multiple files and directories in parallel.
|
||||
A diff output is produced and a sensible exit code is returned.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import difflib
|
||||
import fnmatch
|
||||
import io
|
||||
import errno
|
||||
import multiprocessing
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from functools import partial
|
||||
|
||||
try:
|
||||
from subprocess import DEVNULL # py3k
|
||||
except ImportError:
|
||||
DEVNULL = open(os.devnull, "wb")
|
||||
|
||||
|
||||
DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx'
|
||||
DEFAULT_CLANG_FORMAT_IGNORE = '.clang-format-ignore'
|
||||
|
||||
|
||||
class ExitStatus:
|
||||
SUCCESS = 0
|
||||
DIFF = 1
|
||||
TROUBLE = 2
|
||||
|
||||
def excludes_from_file(ignore_file):
|
||||
excludes = []
|
||||
try:
|
||||
with io.open(ignore_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
# ignore comments
|
||||
continue
|
||||
pattern = line.rstrip()
|
||||
if not pattern:
|
||||
# allow empty lines
|
||||
continue
|
||||
excludes.append(pattern)
|
||||
except EnvironmentError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
return excludes;
|
||||
|
||||
def list_files(files, recursive=False, extensions=None, exclude=None):
|
||||
if extensions is None:
|
||||
extensions = []
|
||||
if exclude is None:
|
||||
exclude = []
|
||||
|
||||
out = []
|
||||
for file in files:
|
||||
if recursive and os.path.isdir(file):
|
||||
for dirpath, dnames, fnames in os.walk(file):
|
||||
fpaths = [os.path.join(dirpath, fname) for fname in fnames]
|
||||
for pattern in exclude:
|
||||
# os.walk() supports trimming down the dnames list
|
||||
# by modifying it in-place,
|
||||
# to avoid unnecessary directory listings.
|
||||
dnames[:] = [
|
||||
x for x in dnames
|
||||
if
|
||||
not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)
|
||||
]
|
||||
fpaths = [
|
||||
x for x in fpaths if not fnmatch.fnmatch(x, pattern)
|
||||
]
|
||||
for f in fpaths:
|
||||
ext = os.path.splitext(f)[1][1:]
|
||||
if ext in extensions:
|
||||
out.append(f)
|
||||
else:
|
||||
out.append(file)
|
||||
return out
|
||||
|
||||
|
||||
def make_diff(file, original, reformatted):
|
||||
return list(
|
||||
difflib.unified_diff(
|
||||
original,
|
||||
reformatted,
|
||||
fromfile='{}\t(original)'.format(file),
|
||||
tofile='{}\t(reformatted)'.format(file),
|
||||
n=3))
|
||||
|
||||
|
||||
class DiffError(Exception):
|
||||
def __init__(self, message, errs=None):
|
||||
super(DiffError, self).__init__(message)
|
||||
self.errs = errs or []
|
||||
|
||||
|
||||
class UnexpectedError(Exception):
|
||||
def __init__(self, message, exc=None):
|
||||
super(UnexpectedError, self).__init__(message)
|
||||
self.formatted_traceback = traceback.format_exc()
|
||||
self.exc = exc
|
||||
|
||||
|
||||
def run_clang_format_diff_wrapper(args, file):
|
||||
try:
|
||||
ret = run_clang_format_diff(args, file)
|
||||
return ret
|
||||
except DiffError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise UnexpectedError('{}: {}: {}'.format(file, e.__class__.__name__,
|
||||
e), e)
|
||||
|
||||
|
||||
def run_clang_format_diff(args, file):
|
||||
try:
|
||||
with io.open(file, 'r', encoding='utf-8') as f:
|
||||
original = f.readlines()
|
||||
except IOError as exc:
|
||||
raise DiffError(str(exc))
|
||||
|
||||
if args.in_place:
|
||||
invocation = [args.clang_format_executable, '-i', file]
|
||||
else:
|
||||
invocation = [args.clang_format_executable, file]
|
||||
|
||||
if args.style:
|
||||
invocation.extend(['--style', args.style])
|
||||
|
||||
if args.dry_run:
|
||||
print(" ".join(invocation))
|
||||
return [], []
|
||||
|
||||
# Use of utf-8 to decode the process output.
|
||||
#
|
||||
# Hopefully, this is the correct thing to do.
|
||||
#
|
||||
# It's done due to the following assumptions (which may be incorrect):
|
||||
# - clang-format will returns the bytes read from the files as-is,
|
||||
# without conversion, and it is already assumed that the files use utf-8.
|
||||
# - if the diagnostics were internationalized, they would use utf-8:
|
||||
# > Adding Translations to Clang
|
||||
# >
|
||||
# > Not possible yet!
|
||||
# > Diagnostic strings should be written in UTF-8,
|
||||
# > the client can translate to the relevant code page if needed.
|
||||
# > Each translation completely replaces the format string
|
||||
# > for the diagnostic.
|
||||
# > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
|
||||
#
|
||||
# It's not pretty, due to Python 2 & 3 compatibility.
|
||||
encoding_py3 = {}
|
||||
if sys.version_info[0] >= 3:
|
||||
encoding_py3['encoding'] = 'utf-8'
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
invocation,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
**encoding_py3)
|
||||
except OSError as exc:
|
||||
raise DiffError(
|
||||
"Command '{}' failed to start: {}".format(
|
||||
subprocess.list2cmdline(invocation), exc
|
||||
)
|
||||
)
|
||||
proc_stdout = proc.stdout
|
||||
proc_stderr = proc.stderr
|
||||
if sys.version_info[0] < 3:
|
||||
# make the pipes compatible with Python 3,
|
||||
# reading lines should output unicode
|
||||
encoding = 'utf-8'
|
||||
proc_stdout = codecs.getreader(encoding)(proc_stdout)
|
||||
proc_stderr = codecs.getreader(encoding)(proc_stderr)
|
||||
# hopefully the stderr pipe won't get full and block the process
|
||||
outs = list(proc_stdout.readlines())
|
||||
errs = list(proc_stderr.readlines())
|
||||
proc.wait()
|
||||
if proc.returncode:
|
||||
raise DiffError(
|
||||
"Command '{}' returned non-zero exit status {}".format(
|
||||
subprocess.list2cmdline(invocation), proc.returncode
|
||||
),
|
||||
errs,
|
||||
)
|
||||
if args.in_place:
|
||||
return [], errs
|
||||
return make_diff(file, original, outs), errs
|
||||
|
||||
|
||||
def bold_red(s):
|
||||
return '\x1b[1m\x1b[31m' + s + '\x1b[0m'
|
||||
|
||||
|
||||
def colorize(diff_lines):
|
||||
def bold(s):
|
||||
return '\x1b[1m' + s + '\x1b[0m'
|
||||
|
||||
def cyan(s):
|
||||
return '\x1b[36m' + s + '\x1b[0m'
|
||||
|
||||
def green(s):
|
||||
return '\x1b[32m' + s + '\x1b[0m'
|
||||
|
||||
def red(s):
|
||||
return '\x1b[31m' + s + '\x1b[0m'
|
||||
|
||||
for line in diff_lines:
|
||||
if line[:4] in ['--- ', '+++ ']:
|
||||
yield bold(line)
|
||||
elif line.startswith('@@ '):
|
||||
yield cyan(line)
|
||||
elif line.startswith('+'):
|
||||
yield green(line)
|
||||
elif line.startswith('-'):
|
||||
yield red(line)
|
||||
else:
|
||||
yield line
|
||||
|
||||
|
||||
def print_diff(diff_lines, use_color):
|
||||
if use_color:
|
||||
diff_lines = colorize(diff_lines)
|
||||
if sys.version_info[0] < 3:
|
||||
sys.stdout.writelines((l.encode('utf-8') for l in diff_lines))
|
||||
else:
|
||||
sys.stdout.writelines(diff_lines)
|
||||
|
||||
|
||||
def print_trouble(prog, message, use_colors):
|
||||
error_text = 'error:'
|
||||
if use_colors:
|
||||
error_text = bold_red(error_text)
|
||||
print("{}: {} {}".format(prog, error_text, message), file=sys.stderr)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
'--clang-format-executable',
|
||||
metavar='EXECUTABLE',
|
||||
help='path to the clang-format executable',
|
||||
default='clang-format')
|
||||
parser.add_argument(
|
||||
'--extensions',
|
||||
help='comma separated list of file extensions (default: {})'.format(
|
||||
DEFAULT_EXTENSIONS),
|
||||
default=DEFAULT_EXTENSIONS)
|
||||
parser.add_argument(
|
||||
'-r',
|
||||
'--recursive',
|
||||
action='store_true',
|
||||
help='run recursively over directories')
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='just print the list of files')
|
||||
parser.add_argument(
|
||||
'-i',
|
||||
'--in-place',
|
||||
action='store_true',
|
||||
help='format file instead of printing differences')
|
||||
parser.add_argument('files', metavar='file', nargs='+')
|
||||
parser.add_argument(
|
||||
'-q',
|
||||
'--quiet',
|
||||
action='store_true',
|
||||
help="disable output, useful for the exit code")
|
||||
parser.add_argument(
|
||||
'-j',
|
||||
metavar='N',
|
||||
type=int,
|
||||
default=0,
|
||||
help='run N clang-format jobs in parallel'
|
||||
' (default number of cpus + 1)')
|
||||
parser.add_argument(
|
||||
'--color',
|
||||
default='auto',
|
||||
choices=['auto', 'always', 'never'],
|
||||
help='show colored diff (default: auto)')
|
||||
parser.add_argument(
|
||||
'-e',
|
||||
'--exclude',
|
||||
metavar='PATTERN',
|
||||
action='append',
|
||||
default=[],
|
||||
help='exclude paths matching the given glob-like pattern(s)'
|
||||
' from recursive search')
|
||||
parser.add_argument(
|
||||
'--style',
|
||||
help='formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# use default signal handling, like diff return SIGINT value on ^C
|
||||
# https://bugs.python.org/issue14229#msg156446
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
try:
|
||||
signal.SIGPIPE
|
||||
except AttributeError:
|
||||
# compatibility, SIGPIPE does not exist on Windows
|
||||
pass
|
||||
else:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
|
||||
colored_stdout = False
|
||||
colored_stderr = False
|
||||
if args.color == 'always':
|
||||
colored_stdout = True
|
||||
colored_stderr = True
|
||||
elif args.color == 'auto':
|
||||
colored_stdout = sys.stdout.isatty()
|
||||
colored_stderr = sys.stderr.isatty()
|
||||
|
||||
version_invocation = [args.clang_format_executable, str("--version")]
|
||||
try:
|
||||
subprocess.check_call(version_invocation, stdout=DEVNULL)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
|
||||
return ExitStatus.TROUBLE
|
||||
except OSError as e:
|
||||
print_trouble(
|
||||
parser.prog,
|
||||
"Command '{}' failed to start: {}".format(
|
||||
subprocess.list2cmdline(version_invocation), e
|
||||
),
|
||||
use_colors=colored_stderr,
|
||||
)
|
||||
return ExitStatus.TROUBLE
|
||||
|
||||
retcode = ExitStatus.SUCCESS
|
||||
|
||||
excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE)
|
||||
excludes.extend(args.exclude)
|
||||
|
||||
files = list_files(
|
||||
args.files,
|
||||
recursive=args.recursive,
|
||||
exclude=excludes,
|
||||
extensions=args.extensions.split(','))
|
||||
|
||||
if not files:
|
||||
return
|
||||
|
||||
njobs = args.j
|
||||
if njobs == 0:
|
||||
njobs = multiprocessing.cpu_count() + 1
|
||||
njobs = min(len(files), njobs)
|
||||
|
||||
if njobs == 1:
|
||||
# execute directly instead of in a pool,
|
||||
# less overhead, simpler stacktraces
|
||||
it = (run_clang_format_diff_wrapper(args, file) for file in files)
|
||||
pool = None
|
||||
else:
|
||||
pool = multiprocessing.Pool(njobs)
|
||||
it = pool.imap_unordered(
|
||||
partial(run_clang_format_diff_wrapper, args), files)
|
||||
pool.close()
|
||||
while True:
|
||||
try:
|
||||
outs, errs = next(it)
|
||||
except StopIteration:
|
||||
break
|
||||
except DiffError as e:
|
||||
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
|
||||
retcode = ExitStatus.TROUBLE
|
||||
sys.stderr.writelines(e.errs)
|
||||
except UnexpectedError as e:
|
||||
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
|
||||
sys.stderr.write(e.formatted_traceback)
|
||||
retcode = ExitStatus.TROUBLE
|
||||
# stop at the first unexpected error,
|
||||
# something could be very wrong,
|
||||
# don't process all files unnecessarily
|
||||
if pool:
|
||||
pool.terminate()
|
||||
break
|
||||
else:
|
||||
sys.stderr.writelines(errs)
|
||||
if outs == []:
|
||||
continue
|
||||
if not args.quiet:
|
||||
print_diff(outs, use_color=colored_stdout)
|
||||
if retcode == ExitStatus.SUCCESS:
|
||||
retcode = ExitStatus.DIFF
|
||||
if pool:
|
||||
pool.join()
|
||||
return retcode
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# These env vars must be set:
|
||||
# SENTRY_PROJECT
|
||||
# SENTRY_AUTH_TOKEN
|
||||
# SENTRY_ORG
|
||||
# SENTRY_ENVIRONMENT
|
||||
# RELEASE_VERSION
|
||||
|
||||
# This script is adapted from getsentry/action-release, a JS GitHub Action. See
|
||||
# the relevant source code here: https://github.com/getsentry/action-release/blob/master/src/main.ts.
|
||||
|
||||
if which sentry-cli >/dev/null; then
|
||||
# Create the new release.
|
||||
ERROR=$(sentry-cli releases new "$RELEASE_VERSION")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "::error title=Error creating Sentry release::$ERROR"
|
||||
fi
|
||||
|
||||
# Set the commits for the release automatically.
|
||||
ERROR=$(sentry-cli releases set-commits --auto "$RELEASE_VERSION")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "::error title=Error setting commits for Sentry release::$ERROR"
|
||||
fi
|
||||
|
||||
# Add a deploy for the release.
|
||||
ERROR=$(sentry-cli deploys new --release "$RELEASE_VERSION" --env "$SENTRY_ENVIRONMENT")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "::error title=Error adding deploy for Sentry release::$ERROR"
|
||||
fi
|
||||
|
||||
# Finalize the release.
|
||||
ERROR=$(sentry-cli releases finalize "$RELEASE_VERSION")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "::error title=Error finalizing Sentry release::$ERROR"
|
||||
fi
|
||||
else
|
||||
NOT_INSTALLED="sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases"
|
||||
echo "::error title=Error creating Sentry release::$NOT_INSTALLED"
|
||||
fi
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# These env vars must be set:
|
||||
# SENTRY_PROJECT
|
||||
# SENTRY_AUTH_TOKEN
|
||||
# SENTRY_ORG
|
||||
# DEBUG_FILE_OR_FOLDER_PATH
|
||||
|
||||
set -x
|
||||
|
||||
if which sentry-cli >/dev/null; then
|
||||
ERROR="$(sentry-cli upload-dif "$DEBUG_FILE_OR_FOLDER_PATH")"
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "warning: sentry-cli - $ERROR"
|
||||
fi
|
||||
else
|
||||
echo "warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases"
|
||||
fi
|
||||
Executable
+266
@@ -0,0 +1,266 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Updates the Info.plist file for a macOS app bundle to register supported file types and URL schemes.
|
||||
# This only applies the updates common to `script/bundle` and `script/run`.
|
||||
# Must be called from the root directory of the warp repo.
|
||||
|
||||
set -e
|
||||
|
||||
if [[ ! -d "script" ]]; then
|
||||
echo "Run this script from the root of your warp repo."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$WARP_PLIST_PATH" ]]; then
|
||||
echo 'Must set $WARP_PLIST_PATH'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$GIT_RELEASE_TAG" ]]; then
|
||||
echo "Updating plist Warp version to $GIT_RELEASE_TAG"
|
||||
plutil -insert WarpVersion -string "$GIT_RELEASE_TAG" "$WARP_PLIST_PATH"
|
||||
|
||||
# We convert our version format to be period-separated integers only to align better with
|
||||
# Apple's guidance on values for `CFBundleVersion` / `CFBundleShortVersionString`.
|
||||
# (They technically only allow [major].[minor].[patch], but it's not enforced.)
|
||||
# This allows IT admins to work with a more stable version of our internal versioning scheme.
|
||||
# A tag like `v0.2025.01.07.08.02.stable_02` would be converted to `0.2025.01.07.08.02.02`.
|
||||
if [[ $GIT_RELEASE_TAG =~ ^v([0-9]+)\.([0-9]{4})\.([0-9]{2})\.([0-9]{2})\.([0-9]{2})\.([0-9]{2})\.[a-z]+_([0-9]{2})$ ]]; then
|
||||
MAJOR="${BASH_REMATCH[1]}"
|
||||
YEAR="${BASH_REMATCH[2]}"
|
||||
MONTH="${BASH_REMATCH[3]}"
|
||||
DAY="${BASH_REMATCH[4]}"
|
||||
HOUR="${BASH_REMATCH[5]}"
|
||||
MINUTE="${BASH_REMATCH[6]}"
|
||||
BUILD="${BASH_REMATCH[7]}"
|
||||
|
||||
FORMATTED_VERSION="${MAJOR}.${YEAR}.${MONTH}.${DAY}.${HOUR}.${MINUTE}.${BUILD}"
|
||||
|
||||
echo "Updating plist version to $FORMATTED_VERSION"
|
||||
plutil -replace CFBundleShortVersionString -string "$FORMATTED_VERSION" "$WARP_PLIST_PATH"
|
||||
plutil -replace CFBundleVersion -string "$FORMATTED_VERSION" "$WARP_PLIST_PATH"
|
||||
else
|
||||
echo "Warning: GIT_RELEASE_TAG '$GIT_RELEASE_TAG' does not match expected format vN.YYYY.MM.DD.HH.MM.channel_NN"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$WARP_SCHEME_NAME" ]]; then
|
||||
echo "Updating plist to support a custom url scheme"
|
||||
# Update the plist that cargo bundle creates to add support for custom URL schemes. Unfortunately, cargo bundle does not support
|
||||
# setting arbitrary plist fields, so we must do this after the fact.
|
||||
plutil -insert CFBundleURLTypes -xml "<array><dict><key>CFBundleURLName</key><string>Custom App</string><key>CFBundleURLSchemes</key><array><string>$WARP_SCHEME_NAME</string></array></dict></array>" "$WARP_PLIST_PATH"
|
||||
fi
|
||||
|
||||
if [[ -z "$WARP_PLIST_NO_FILE_TYPES" ]]; then
|
||||
echo "Updating plist with supported file types"
|
||||
plutil -insert CFBundleDocumentTypes -xml "
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Folder</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.folder</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Terminal shell script</string>
|
||||
<key>CFBundleTypeRole</key><string>Shell</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.terminal.shell-script</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Unix Executable File</string>
|
||||
<key>CFBundleTypeRole</key><string>Shell</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.unix-executable</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Markdown File</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>net.daringfireball.markdown</string>
|
||||
<string>com.unknown.md</string>
|
||||
<string>net.ia.markdown</string>
|
||||
<string>public.markdown</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Plain Text File</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Source Code File</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.source-code</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>Source Code</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSHandlerRank</key><string>Alternate</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>c</string>
|
||||
<string>h</string>
|
||||
<string>cc</string>
|
||||
<string>cpp</string>
|
||||
<string>cxx</string>
|
||||
<string>c++</string>
|
||||
<string>hpp</string>
|
||||
<string>hxx</string>
|
||||
<string>hh</string>
|
||||
<string>h++</string>
|
||||
<string>m</string>
|
||||
<string>mm</string>
|
||||
<string>cs</string>
|
||||
<string>csx</string>
|
||||
<string>css</string>
|
||||
<string>dart</string>
|
||||
<string>dockerfile</string>
|
||||
<string>containerfile</string>
|
||||
<string>go</string>
|
||||
<string>htm</string>
|
||||
<string>html</string>
|
||||
<string>xhtml</string>
|
||||
<string>java</string>
|
||||
<string>jav</string>
|
||||
<string>js</string>
|
||||
<string>mjs</string>
|
||||
<string>cjs</string>
|
||||
<string>json</string>
|
||||
<string>jsx</string>
|
||||
<string>less</string>
|
||||
<string>lua</string>
|
||||
<string>makefile</string>
|
||||
<string>mk</string>
|
||||
<string>cmake</string>
|
||||
<string>php</string>
|
||||
<string>pl</string>
|
||||
<string>pm</string>
|
||||
<string>py</string>
|
||||
<string>pyi</string>
|
||||
<string>r</string>
|
||||
<string>rb</string>
|
||||
<string>gemspec</string>
|
||||
<string>rs</string>
|
||||
<string>rst</string>
|
||||
<string>sass</string>
|
||||
<string>scss</string>
|
||||
<string>sql</string>
|
||||
<string>swift</string>
|
||||
<string>ts</string>
|
||||
<string>tsx</string>
|
||||
<string>txt</string>
|
||||
<string>vue</string>
|
||||
<string>xml</string>
|
||||
<string>xaml</string>
|
||||
<string>dtd</string>
|
||||
<string>plist</string>
|
||||
<string>yaml</string>
|
||||
<string>yml</string>
|
||||
<string>eyaml</string>
|
||||
<string>eyml</string>
|
||||
<string>toml</string>
|
||||
<string>cfg</string>
|
||||
<string>conf</string>
|
||||
<string>config</string>
|
||||
<string>csv</string>
|
||||
<string>diff</string>
|
||||
<string>env</string>
|
||||
<string>gradle</string>
|
||||
<string>groovy</string>
|
||||
<string>ini</string>
|
||||
<string>log</string>
|
||||
<string>properties</string>
|
||||
<string>svg</string>
|
||||
<string>tex</string>
|
||||
<string>cls</string>
|
||||
<string>lock</string>
|
||||
<string>bat</string>
|
||||
<string>cmd</string>
|
||||
<string>ps1</string>
|
||||
<string>psd1</string>
|
||||
<string>psm1</string>
|
||||
<string>sh</string>
|
||||
<string>bash</string>
|
||||
<string>zsh</string>
|
||||
<string>fish</string>
|
||||
<string>bashrc</string>
|
||||
<string>bash_profile</string>
|
||||
<string>zshrc</string>
|
||||
<string>zshenv</string>
|
||||
<string>profile</string>
|
||||
<string>gitignore</string>
|
||||
<string>gitconfig</string>
|
||||
<string>gitattributes</string>
|
||||
<string>editorconfig</string>
|
||||
<string>hbs</string>
|
||||
<string>handlebars</string>
|
||||
<string>erb</string>
|
||||
<string>tf</string>
|
||||
<string>tfvars</string>
|
||||
<string>proto</string>
|
||||
<string>graphql</string>
|
||||
<string>gql</string>
|
||||
<string>wgsl</string>
|
||||
<string>zig</string>
|
||||
<string>kt</string>
|
||||
<string>kts</string>
|
||||
<string>scala</string>
|
||||
<string>ex</string>
|
||||
<string>exs</string>
|
||||
<string>erl</string>
|
||||
<string>clj</string>
|
||||
<string>cljs</string>
|
||||
<string>coffee</string>
|
||||
<string>fs</string>
|
||||
<string>fsi</string>
|
||||
<string>fsx</string>
|
||||
<string>ml</string>
|
||||
<string>mli</string>
|
||||
<string>vb</string>
|
||||
<string>pug</string>
|
||||
<string>jade</string>
|
||||
<string>ipynb</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key><string>All Documents</string>
|
||||
<key>CFBundleTypeRole</key><string>Editor</string>
|
||||
<key>LSHandlerRank</key><string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>" "$WARP_PLIST_PATH"
|
||||
fi
|
||||
|
||||
# Unfortunately, there isn't a single standard UTI (what goes in LSItemContentTypes) for Markdown
|
||||
# files. Instead, we list all the common ones - see these for more info:
|
||||
# https://github.com/sbarex/QLMarkdown#installation
|
||||
# https://blog.smittytone.net/2021/01/19/how-tools-try-to-own-markdown/
|
||||
|
||||
echo "Updating plist with permissions descriptions"
|
||||
plutil -insert NSAppleEventsUsageDescription -string "A program in Warp wants to use AppleScript." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSCameraUsageDescription -string "A program in Warp wants to use the camera." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSMicrophoneUsageDescription -string "A program in Warp wants to use your microphone." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSContactsUsageDescription -string "A program in Warp wants to use your contacts." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSCalendarsUsageDescription -string "A program in Warp wants to use your calendar." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSLocationUsageDescription -string "A program in Warp wants to use your location information." "$WARP_PLIST_PATH"
|
||||
plutil -insert NSPhotoLibraryUsageDescription -string "A program in Warp wants to use your photo library." "$WARP_PLIST_PATH"
|
||||
|
||||
echo "Disabling use of Liquid Glass on macOS Tahoe"
|
||||
plutil -insert UIDesignRequiresCompatibility -bool true "$WARP_PLIST_PATH"
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.2598 3.22483C12.2526 3.23454 12.2495 3.24758 12.2432 3.27366L8.70963 17.879C8.6999 17.9192 8.69504 17.9393 8.70002 17.9551C8.70438 17.969 8.71366 17.9808 8.72611 17.9883C8.74031 17.9969 8.761 17.9969 8.80236 17.9969H20.5658C21.9102 17.9969 23 16.8707 23 15.4815V5.71604C23 4.32685 21.9102 3.20068 20.5658 3.20068H12.3359C12.3091 3.20068 12.2957 3.20068 12.2845 3.20539C12.2747 3.20954 12.2662 3.21626 12.2598 3.22483Z" fill="#FF0000"/>
|
||||
<path d="M10.0849 6.04543C10.0899 6.06122 10.085 6.08129 10.0754 6.12145L6.91285 19.3058C6.90322 19.3459 6.8984 19.366 6.9034 19.3818C6.90778 19.3956 6.91706 19.4074 6.9295 19.4149C6.94369 19.4234 6.96433 19.4234 7.00563 19.4234H11.8568C11.8981 19.4234 11.9187 19.4234 11.9329 19.432C11.9454 19.4395 11.9547 19.4512 11.959 19.4651C11.964 19.4809 11.9592 19.5009 11.9496 19.5411L11.6652 20.7268C11.6589 20.7529 11.6558 20.766 11.6486 20.7758C11.6422 20.7843 11.6337 20.7911 11.6239 20.7952C11.6127 20.8 11.5993 20.8 11.5724 20.8H3.41346C2.08054 20.8 1 19.6738 1 18.2846V8.51913C1 7.12995 2.08054 6.00378 3.41346 6.00378H9.98264C10.0239 6.00378 10.0446 6.00378 10.0588 6.01233C10.0712 6.01981 10.0805 6.03158 10.0849 6.04543Z" fill="#FF0000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
Executable
+171
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds a Warp binary and bundles it up for distribution.
|
||||
|
||||
set -e
|
||||
|
||||
# On macOS, we need to use LLVM's clang for wasm32-unknown-unknown target
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
LLVM_CLANG="/opt/homebrew/opt/llvm/bin/clang"
|
||||
if [ ! -f "$LLVM_CLANG" ]; then
|
||||
echo "Error: LLVM clang not found at $LLVM_CLANG"
|
||||
echo "Please install it with: brew install llvm"
|
||||
exit 1
|
||||
fi
|
||||
export CC_wasm32_unknown_unknown="$LLVM_CLANG"
|
||||
fi
|
||||
|
||||
WORKSPACE_ROOT_DIR="$(pwd)"
|
||||
CARGO_TARGET_DIR="$WORKSPACE_ROOT_DIR/target/wasm32-unknown-unknown"
|
||||
|
||||
# By default we build dev bundles.
|
||||
RELEASE_CHANNEL="dev"
|
||||
# TODO: We should enable crash_reporting and before enabling for trusted testers.
|
||||
# https://linear.app/warpdotdev/issue/PLAT-428/crash-reporting-on-web
|
||||
FEATURES="release_bundle,gui"
|
||||
DEBUG=false
|
||||
|
||||
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
|
||||
;;
|
||||
--no-split)
|
||||
NO_SPLIT="true"
|
||||
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
|
||||
;;
|
||||
--features)
|
||||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
|
||||
echo "Setting features to $2"
|
||||
FEATURES_OVERRIDE=$2
|
||||
shift 2
|
||||
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-wasm"
|
||||
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).
|
||||
CARGO_PROFILE="release-wasm-debug_assertions"
|
||||
else
|
||||
CARGO_PROFILE="release-wasm"
|
||||
fi
|
||||
|
||||
# The `dev` profile needs to be special-cased for historical
|
||||
# reasons: https://doc.rust-lang.org/cargo/guide/build-cache.html#build-cache.
|
||||
# We don't use the `dev` profile today when bundling for wasm
|
||||
# but this is left in the script to future-proof the use of the `dev` profile.
|
||||
if [[ "$CARGO_PROFILE" == "dev" ]]; then
|
||||
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_DIR/debug"
|
||||
else
|
||||
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_DIR/$CARGO_PROFILE"
|
||||
fi
|
||||
|
||||
OUT_DIR="$CARGO_TARGET_OUTPUT_DIR/bundle/wasm"
|
||||
mkdir -p "$OUT_DIR"
|
||||
ASSET_TARGET_DIR="$CARGO_TARGET_OUTPUT_DIR/bundle/assets"
|
||||
mkdir -p "$ASSET_TARGET_DIR"
|
||||
EXTRAS_DIR="$CARGO_TARGET_OUTPUT_DIR/bundle/extras"
|
||||
mkdir -p "$EXTRAS_DIR"
|
||||
|
||||
# Update parameters based on the target release channel.
|
||||
#
|
||||
# WARP_BIN is the name of the binary produced by cargo.
|
||||
# N.B. The bundled outputs will always be warp.js and warp_bg.wasm.
|
||||
if [[ $RELEASE_CHANNEL = "local" ]]; then
|
||||
WARP_BIN="warp"
|
||||
FEATURES="$FEATURES,remote_tty"
|
||||
elif [[ $RELEASE_CHANNEL = "dev" ]]; then
|
||||
WARP_BIN="dev"
|
||||
elif [[ $RELEASE_CHANNEL = "preview" ]]; then
|
||||
WARP_BIN="preview"
|
||||
FEATURES="$FEATURES,preview_channel"
|
||||
elif [[ $RELEASE_CHANNEL = "stable" ]]; then
|
||||
WARP_BIN="stable"
|
||||
fi
|
||||
|
||||
if [ -n "${FEATURES_OVERRIDE+x}" ]; then
|
||||
FEATURES="$FEATURES_OVERRIDE"
|
||||
fi
|
||||
|
||||
# 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
|
||||
ASSET_TARGET_DIR="$ASSET_TARGET_DIR" cargo check --target wasm32-unknown-unknown --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build the wasm binary.
|
||||
echo "Building and bundling Warp for channel $RELEASE_CHANNEL"
|
||||
ASSET_TARGET_DIR="$ASSET_TARGET_DIR" cargo build --target wasm32-unknown-unknown --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES"
|
||||
|
||||
# Generate linked JS and wasm files that can be run in the browser.
|
||||
echo "Running wasm-bindgen on $CARGO_TARGET_OUTPUT_DIR/$WARP_BIN.wasm"
|
||||
wasm-bindgen --target web --out-dir "$OUT_DIR" --out-name "warp" --keep-debug --no-typescript "$CARGO_TARGET_OUTPUT_DIR/$WARP_BIN.wasm"
|
||||
|
||||
WASM_BINARY_OUT="${OUT_DIR}/warp_bg.wasm"
|
||||
WASM_BINARY_DEBUG="${EXTRAS_DIR}/warp_bg.debug.wasm"
|
||||
|
||||
if [[ "$NO_SPLIT" != "true" ]]; then
|
||||
# Run Sentry's wasm-split binary to separate out debug information from the
|
||||
# wasm binary.
|
||||
#
|
||||
# Binary releases of wasm-split can be found here:
|
||||
# https://github.com/getsentry/symbolicator/releases
|
||||
wasm-split "$WASM_BINARY_OUT" --debug-out "$WASM_BINARY_DEBUG" --strip --strip-names
|
||||
fi
|
||||
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# directory containing all built packages.
|
||||
if [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
echo "::echo::on"
|
||||
echo "packages_dir=$OUT_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "assets_dir=$ASSET_TARGET_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "debug_executable_path=$WASM_BINARY_DEBUG" >> "$GITHUB_OUTPUT"
|
||||
echo "::echo::off"
|
||||
fi
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>warp</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0px;
|
||||
background-color: black;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
height: 100svh;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="module">
|
||||
import init from "/assets/client/wasm/warp.js";
|
||||
window.addEventListener("load", () => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install all dependencies required to build Warp on Web.
|
||||
|
||||
set -e
|
||||
|
||||
# Install Rust and other development libraries needed in the compilation
|
||||
# process. Web and Linux are both built on Linux runners, so we can reuse
|
||||
# the linux build dependencies script here.
|
||||
"$PWD"/script/linux/install_build_deps
|
||||
|
||||
# Make sure the wasm Rust toolchain target is available.
|
||||
rustup target add wasm32-unknown-unknown
|
||||
|
||||
# Install cargo-managed dependencies required to build for wasm.
|
||||
# The wasm-bindgen version installed here must be kept in sync with the version
|
||||
# used in the project.
|
||||
WASM_BINDGEN_VERSION="$(cargo metadata --format-version 1 | jq -rc '.packages[] | select(.name == "wasm-bindgen") | .version')"
|
||||
cargo binstall --force -y wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION"
|
||||
cargo binstall --force -y wasm-opt
|
||||
|
||||
# Install wasm-split binary.
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
cd "$HOME/.local/bin"
|
||||
curl -L https://github.com/getsentry/symbolicator/releases/download/26.3.1/wasm-split-Linux-x86_64 -o wasm-split
|
||||
chmod +x wasm-split
|
||||
# Export .local/bin into the path so we can find the wasm-split binary.
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
WASM_BINDGEN_VERSION="$(cargo metadata --format-version 1 | jq -rc '.packages[] | select(.name == "wasm-bindgen") | .version')"
|
||||
cargo install wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION"
|
||||
|
||||
# We source the bundle script so we can access it's calculated $OUT_DIR.
|
||||
source ./script/wasm/bundle --debug --no-split --channel local "$@"
|
||||
|
||||
BUNDLE_DIR="$(dirname "$OUT_DIR")"
|
||||
|
||||
cp "$WORKSPACE_ROOT_DIR/script/wasm/dev-index.html" "$BUNDLE_DIR/index.html"
|
||||
echo "Built Warp to $BUNDLE_DIR"
|
||||
|
||||
cargo run --release --package serve-wasm -- "$BUNDLE_DIR"
|
||||
@@ -0,0 +1,73 @@
|
||||
# Inno Setup installer script
|
||||
|
||||
## What is `windows-installer.iss`?
|
||||
|
||||
On Windows, programs are conventionally installed using an installer, also known as an installation wizard.
|
||||
The installer is a single executable that takes care of:
|
||||
* Creating a directory to store the program's files
|
||||
* Downloading assets
|
||||
* Initializing registry entries
|
||||
* Creating a desktop icon
|
||||
* ... and more, depending on the application's needs.
|
||||
|
||||
|
||||
`windows-installer.iss` is an **Inno Setup script**:
|
||||
a configuration file for building a Warp installer.
|
||||
The Inno Setup Compiler takes a script file and generates an installer executable.
|
||||
This is roughly equivalent to the bundling process on MacOS.
|
||||
|
||||
|
||||
## How to edit the installer
|
||||
|
||||
See the Inno Setup documentation: [Inno Setup Help](https://jrsoftware.org/ishelp/).
|
||||
This script can be edited manually using any code editor.
|
||||
However, it requires the Inno Setup compiler to be turned into a `.exe` file.
|
||||
|
||||
|
||||
## How to compile this installer
|
||||
|
||||
First, ensure you've set up your environment.
|
||||
* Download and install the [Inno Setup Compiler](https://jrsoftware.org/isdl.php).
|
||||
* Run `cargo build` to ensure the installer uses the latest version of Warp.
|
||||
|
||||
### Option 1: Use the CLI
|
||||
1. Add the Inno Setup Command-line Compiler executable to your shell path.
|
||||
By default, it is located at `C:\Program Files (x86)\Inno Setup 6\ISCC.exe`.
|
||||
2. Compile the installer:
|
||||
```shell
|
||||
iscc .\script\windows\windows-installer.iss`.
|
||||
```
|
||||
3. Run the generated executable:
|
||||
```shell
|
||||
.\script\windows\Output\Warp-Windows-Setup.exe`.
|
||||
```
|
||||
|
||||
The script begins with a series of preprocessor definitions.
|
||||
From the command line, use the `/D` flag to emulate preprocessor definitions
|
||||
and override the hardcoded defaults.
|
||||
Usage: `iscc <script path> /D<name>[=<value>]`
|
||||
|
||||
The following constants can be overwritten:
|
||||
* `MyAppVersion` (default: `0.1.0`)
|
||||
* `MyAppExeName` (default: `warp.exe`)
|
||||
* `ReleaseChannel` (default: `dev`)
|
||||
* `TargetProfileDir` (default: `debug`)
|
||||
|
||||
### Option 2: Use the GUI
|
||||
1. Open the Inno Setup application and select this script.
|
||||
2. Click the "compile" button. This will generate an installer executable in a directory called `Output` at the same level as this script.
|
||||
2. To run the installer, click the "run" button in Inno Setup.
|
||||
|
||||
|
||||
## Using icons
|
||||
|
||||
Windows has its own icon file format that bundles together multiple icon sizes.
|
||||
App icons are located in `app/channels/<channel_name>/icon/no-padding`.
|
||||
The `.ico` files are generated using imagemagick:
|
||||
|
||||
```shell
|
||||
convert 16x16.png 32x32.png 48x48.png 64x64.png 256x256.png icon.ico
|
||||
```
|
||||
|
||||
Note that sizes above 256x256 are not supported.
|
||||
See the [Inno Setup docs](https://jrsoftware.org/ishelp/index.php?topic=setup_setupiconfile).
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env powershell
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Git for Windows can be installed system-wide (Program Files) or per-user (LOCALAPPDATA\Programs\Git).
|
||||
$gitBinCandidates = @(
|
||||
"$env:PROGRAMFILES\Git\bin",
|
||||
"$env:LOCALAPPDATA\Programs\Git\bin"
|
||||
)
|
||||
$gitBinDir = $gitBinCandidates | Where-Object { Test-Path -PathType Container $_ } | Select-Object -First 1
|
||||
if (-not $gitBinDir) {
|
||||
Write-Error 'Git for Windows is required. Please install it at:'
|
||||
Write-Error 'https://gitforwindows.org/'
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Get-Command -Name cargo -Type Application -ErrorAction SilentlyContinue)) {
|
||||
Write-Output 'Installing rust...'
|
||||
Invoke-WebRequest -Uri 'https://win.rustup.rs/x86_64' -OutFile "$env:Temp\rustup-init.exe"
|
||||
& "$env:Temp\rustup-init.exe"
|
||||
Write-Output 'Please start a new terminal session so that cargo is in your PATH'
|
||||
exit 1
|
||||
}
|
||||
|
||||
# A bash executable should come with Git for Windows
|
||||
& "$gitBinDir\bash.exe" "$PWD\script\install_cargo_test_deps"
|
||||
|
||||
# Needed in wasm compilation for parsing the version of wasm-bindgen
|
||||
winget install jqlang.jq
|
||||
|
||||
# CMake is needed to build some dependencies, e.g.: sentry-contrib-native.
|
||||
winget install -e --id Kitware.CMake
|
||||
|
||||
# We use InnoSetup to build our release bundle installer.
|
||||
winget install -e --id JRSoftware.InnoSetup
|
||||
|
||||
# If we don't see gcloud command, try adding the install location to the PATH.
|
||||
if (-not (Get-Command -Name gcloud -Type Application -ErrorAction SilentlyContinue)) {
|
||||
$env:PATH += ";$env:LOCALAPPDATA\Google\Cloud SDK\google-cloud-sdk\bin"
|
||||
}
|
||||
|
||||
# If we still don't see it, install it.
|
||||
if (-not (Get-Command -Name gcloud -Type Application -ErrorAction SilentlyContinue)) {
|
||||
(New-Object Net.WebClient).DownloadFile('https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe', "$env:Temp\GoogleCloudSDKInstaller.exe")
|
||||
Start-Process "$env:Temp\GoogleCloudSDKInstaller.exe" -Wait
|
||||
}
|
||||
|
||||
[string]$identityToken = gcloud auth print-identity-token
|
||||
if ($identityToken.Trim().Length -eq 0) {
|
||||
Write-Output 'gcloud CLI authentication missing. Press enter to continue...'
|
||||
Read-Host
|
||||
gcloud auth login
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env powershell
|
||||
#
|
||||
# Bundle the application for release.
|
||||
|
||||
Param (
|
||||
# Build dev bundles by default.
|
||||
[Switch]$DEBUG_BUILD = $False,
|
||||
|
||||
[Alias('check-only')]
|
||||
[Switch]$CHECK_ONLY,
|
||||
|
||||
[ValidateSet('local', 'dev', 'preview', 'stable')]
|
||||
[String]$CHANNEL = 'dev',
|
||||
|
||||
[Alias('release-tag')]
|
||||
[String]$RELEASE_TAG = '',
|
||||
[String]$FEATURES = 'release_bundle,crash_reporting,gui',
|
||||
|
||||
# Builds only the Warp binary, skips the installer.
|
||||
[Switch]$SKIP_BUILD_INSTALLER = $False,
|
||||
# Builds only the installer, skips the Warp binary. Use this if the Warp
|
||||
# binary has already been built.
|
||||
[Switch]$SKIP_BUILD_BINARY = $False,
|
||||
|
||||
[ValidateSet('x64', 'arm64')]
|
||||
[String]$ARCH = '',
|
||||
|
||||
# A signtool command for Inno Setup to sign the setup engine and uninstaller.
|
||||
# Uses $f as the file placeholder, e.g.:
|
||||
# 'signtool.exe sign /fd SHA256 ... $f'
|
||||
# When empty, the installer is built without signing.
|
||||
[Alias('sign-tool-cmd')]
|
||||
[String]$SIGN_TOOL_CMD = ''
|
||||
)
|
||||
|
||||
if ($RELEASE_TAG) {
|
||||
$env:GIT_RELEASE_TAG = $RELEASE_TAG
|
||||
}
|
||||
|
||||
# Use provided ARCH parameter if set, otherwise detect from system
|
||||
if (-not $ARCH) {
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {
|
||||
$ARCH = 'x64'
|
||||
} elseif ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
|
||||
$ARCH = 'arm64'
|
||||
} else {
|
||||
throw "Unsupported processor architecture: $env:PROCESSOR_ARCHITECTURE"
|
||||
}
|
||||
}
|
||||
|
||||
if ($ARCH -eq 'arm64') {
|
||||
$FILE_ENDING = 'Setup-arm64'
|
||||
$PLATFORM_TARGET = 'aarch64-pc-windows-msvc'
|
||||
} else {
|
||||
# If x64, then we just use the filename "WarpSetup.exe" for example
|
||||
$FILE_ENDING = 'Setup'
|
||||
$PLATFORM_TARGET = 'x86_64-pc-windows-msvc'
|
||||
}
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$WORKSPACE_ROOT_DIR = $(Get-Location).Path
|
||||
$CARGO_TARGET_DIR = $WORKSPACE_ROOT_DIR + '\target'
|
||||
$WINDOWS_INSTALLER_DIR = $WORKSPACE_ROOT_DIR + '\script\windows'
|
||||
|
||||
if ($DEBUG_BUILD) {
|
||||
$CARGO_PROFILE = 'dev'
|
||||
} elseif (("$CHANNEL" -eq 'local') -or ("$CHANNEL" -eq 'dev')) {
|
||||
# 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).
|
||||
$CARGO_PROFILE = 'rltoda'
|
||||
} else {
|
||||
$CARGO_PROFILE = 'rlto'
|
||||
}
|
||||
|
||||
if ($CARGO_PROFILE -eq 'dev') {
|
||||
$CARGO_TARGET_OUTPUT_DIR = "$CARGO_TARGET_DIR" + '\' + $PLATFORM_TARGET + '\debug'
|
||||
} else {
|
||||
$CARGO_TARGET_OUTPUT_DIR = "$CARGO_TARGET_DIR" + '\' + $PLATFORM_TARGET + '\' + "$CARGO_PROFILE"
|
||||
}
|
||||
$BUNDLE_ID = "dev.warp.$app_name"
|
||||
|
||||
# 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 ("$CHANNEL" -eq 'local') {
|
||||
$WARP_BIN = 'warp'
|
||||
$BINARY_NAME = 'warp.exe'
|
||||
$APP_NAME = 'WarpLocal'
|
||||
$FEATURES = "$FEATURES,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'dev') {
|
||||
$WARP_BIN = 'dev'
|
||||
$BINARY_NAME = 'dev.exe'
|
||||
$APP_NAME = 'WarpDev'
|
||||
$FEATURES = "$FEATURES,agent_mode_debug,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'preview') {
|
||||
$WARP_BIN = 'preview'
|
||||
$BINARY_NAME = 'preview.exe'
|
||||
$APP_NAME = 'WarpPreview'
|
||||
$FEATURES = "$FEATURES,preview_channel,nld_improvements"
|
||||
} elseif ("$CHANNEL" -eq 'stable') {
|
||||
$WARP_BIN = 'stable'
|
||||
$BINARY_NAME = 'warp.exe'
|
||||
$APP_NAME = 'Warp'
|
||||
# TODO(vorporeal): Remove this once we get tests passing with this default enabled.
|
||||
$FEATURES = "$FEATURES,nld_improvements"
|
||||
}
|
||||
|
||||
$BINARY_PATH = "$CARGO_TARGET_OUTPUT_DIR\$BINARY_NAME"
|
||||
$BUNDLE_ID = "dev.warp.$APP_NAME"
|
||||
$INSTALLER_OUTPUT_DIR = "$WINDOWS_INSTALLER_DIR\Output"
|
||||
$INSTALLER_NAME = "$($APP_NAME)$($FILE_ENDING)"
|
||||
$INSTALLER_PATH = "$($INSTALLER_OUTPUT_DIR)\$($INSTALLER_NAME).exe"
|
||||
$PDB_PATH = "$CARGO_TARGET_OUTPUT_DIR\$WARP_BIN.pdb"
|
||||
|
||||
# The CARGO_FULL_PROFILE environment variable is read by the `cargo` build
|
||||
# script (`app/build.rs`) to determine where to place `conpty.dll`.
|
||||
if ($DEBUG_BUILD) {
|
||||
$env:CARGO_FULL_PROFILE = 'debug'
|
||||
} else {
|
||||
$env:CARGO_FULL_PROFILE = $CARGO_PROFILE
|
||||
}
|
||||
|
||||
# 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) {
|
||||
cargo check -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" --target $PLATFORM_TARGET
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to verify Warp $WARP_BIN compilation with profile $CARGO_PROFILE"
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-Not $SKIP_BUILD_BINARY) {
|
||||
Write-Output "Building Warp for channel $CHANNEL and bundle id $BUNDLE_ID"
|
||||
$env:CARGO_BIN_NAME = $CHANNEL
|
||||
$env:WARP_APP_NAME = $APP_NAME
|
||||
cargo build -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" --target $PLATFORM_TARGET
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to build Warp $WARP_BIN binary with profile $CARGO_PROFILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If we desire an executable name different from the cargo bin, rename it.
|
||||
if ("$WARP_BIN.exe" -ne $BINARY_NAME) {
|
||||
$binarySource = "$CARGO_TARGET_OUTPUT_DIR\$WARP_BIN.exe"
|
||||
Write-Output "Renaming executable $WARP_BIN.exe to $BINARY_NAME"
|
||||
Move-Item -Path "$binarySource" -Destination "$BINARY_PATH" -Force
|
||||
}
|
||||
}
|
||||
|
||||
if ($SKIP_BUILD_INSTALLER) {
|
||||
# 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.
|
||||
if ($env:GITHUB_ACTIONS -eq 'true') {
|
||||
Write-Output '::echo::on'
|
||||
"target_profile_dir=$CARGO_TARGET_OUTPUT_DIR" >> "$env:GITHUB_OUTPUT"
|
||||
"binary_path=$BINARY_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
Write-Output '::echo::off'
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Output "Built for $ARCH with executable at $BINARY_PATH"
|
||||
|
||||
# Prepare bundled resources
|
||||
$BUNDLED_RESOURCES_DIR = "$CARGO_TARGET_OUTPUT_DIR\resources"
|
||||
Write-Output "Preparing bundled resources..."
|
||||
& "$WINDOWS_INSTALLER_DIR\prepare_bundled_resources.ps1" -DestinationDir "$BUNDLED_RESOURCES_DIR" -Channel "$CHANNEL" -CargoProfile "$CARGO_PROFILE"
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to prepare bundled resources"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output 'Building Warp installer'
|
||||
$ISCC_ARGS = @(
|
||||
"$WINDOWS_INSTALLER_DIR\windows-installer.iss",
|
||||
"/DReleaseChannel=$CHANNEL",
|
||||
"/DMyAppExeName=$BINARY_NAME",
|
||||
"/DTargetProfileDir=$CARGO_TARGET_OUTPUT_DIR",
|
||||
"/DMyAppName=$APP_NAME",
|
||||
"/DMyAppVersion=$env:GIT_RELEASE_TAG",
|
||||
"/DArch=$ARCH",
|
||||
"/DOutputName=$INSTALLER_NAME"
|
||||
)
|
||||
# Also accept the sign tool command via env var
|
||||
if (-not $SIGN_TOOL_CMD -and $env:SIGN_TOOL_CMD) {
|
||||
$SIGN_TOOL_CMD = $env:SIGN_TOOL_CMD
|
||||
}
|
||||
if ($SIGN_TOOL_CMD) {
|
||||
$ISCC_ARGS += '/DSIGN_TOOL=1'
|
||||
$ISCC_ARGS += "/Scodesign=$SIGN_TOOL_CMD"
|
||||
}
|
||||
& ISCC @ISCC_ARGS
|
||||
if (-Not $?) {
|
||||
Write-Error "Failed to build $APP_NAME installer"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If this is being run within a GitHub action, set an output variable with the
|
||||
# location of the installer so it can be referenced by subsequent actions.
|
||||
if ($env:GITHUB_ACTIONS -eq 'true') {
|
||||
Write-Output '::echo::on'
|
||||
$INSTALLER_PATH = $INSTALLER_PATH -replace '\\', '/'
|
||||
"installer_path=$INSTALLER_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
"pdb_file_path=$PDB_PATH" >> "$env:GITHUB_OUTPUT"
|
||||
Write-Output '::echo::off'
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
[Code]
|
||||
{ Adapted from https://stackoverflow.com/a/46609047 }
|
||||
|
||||
const
|
||||
SystemEnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
|
||||
UserEnvironmentKey = 'Environment';
|
||||
|
||||
{ Get the appropriate registry key and root based on install mode. }
|
||||
procedure GetEnvironmentKeyInfo(var RootKey: Integer; var SubKey: string);
|
||||
begin
|
||||
if IsAdminInstallMode then begin
|
||||
RootKey := HKEY_LOCAL_MACHINE;
|
||||
SubKey := SystemEnvironmentKey;
|
||||
end else begin
|
||||
RootKey := HKEY_CURRENT_USER;
|
||||
SubKey := UserEnvironmentKey;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Add path to environment PATH variable. }
|
||||
procedure EnvAddPath(Path: string);
|
||||
var
|
||||
Paths: string;
|
||||
RootKey: Integer;
|
||||
SubKey: string;
|
||||
begin
|
||||
{ Get the appropriate registry location }
|
||||
GetEnvironmentKeyInfo(RootKey, SubKey);
|
||||
|
||||
{ Retrieve current path (use empty string if entry not exists) }
|
||||
if not RegQueryStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Paths := '';
|
||||
|
||||
{ Skip if string already found in path }
|
||||
if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
|
||||
|
||||
{ Append string to the end of the path variable }
|
||||
Paths := Paths + ';'+ Path +';'
|
||||
|
||||
{ Overwrite (or create if missing) path environment variable }
|
||||
if RegWriteStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Log(Format('Added [%s] to PATH: [%s]', [Path, Paths]))
|
||||
else Log(Format('Error adding [%s] to PATH: [%s]', [Path, Paths]));
|
||||
end;
|
||||
|
||||
{ Remove path from environment PATH variable. }
|
||||
procedure EnvRemovePath(Path: string);
|
||||
var
|
||||
Paths: string;
|
||||
P: Integer;
|
||||
RootKey: Integer;
|
||||
SubKey: string;
|
||||
begin
|
||||
{ Get the appropriate registry location }
|
||||
GetEnvironmentKeyInfo(RootKey, SubKey);
|
||||
|
||||
{ Skip if registry entry not exists }
|
||||
if not RegQueryStringValue(RootKey, SubKey, 'Path', Paths) then
|
||||
exit;
|
||||
|
||||
{ Skip if string not found in path }
|
||||
P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
|
||||
if P = 0 then exit;
|
||||
|
||||
{ Update path variable }
|
||||
Delete(Paths, P - 1, Length(Path) + 1);
|
||||
|
||||
{ Overwrite path environment variable }
|
||||
if RegWriteStringValue(RootKey, SubKey, 'Path', Paths)
|
||||
then Log(Format('Removed [%s] from PATH: [%s]', [Path, Paths]))
|
||||
else Log(Format('Error removing [%s] from PATH: [%s]', [Path, Paths]));
|
||||
end;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env powershell
|
||||
#
|
||||
# Install all dependencies required to build Warp on Windows.
|
||||
|
||||
# Install Rust + cargo.
|
||||
bash (((Get-Location).path) + '\script\install_rust')
|
||||
|
||||
# Install various build-time dependencies through cargo.
|
||||
bash (((Get-Location).path) + '\script\install_cargo_build_deps')
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,183 @@
|
||||
#
|
||||
# Prepares bundled resources for distribution on Windows.
|
||||
#
|
||||
# This script copies resources that should be bundled with Warp into a
|
||||
# destination directory. It is used by the Windows build script.
|
||||
#
|
||||
# Usage:
|
||||
# prepare_bundled_resources.ps1 <destination_directory>
|
||||
#
|
||||
# Arguments:
|
||||
# destination_directory: The directory where resources should be installed.
|
||||
# Resources will be copied to subdirectories within
|
||||
# this path (e.g., $DEST_DIR\skills).
|
||||
|
||||
Param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String]$DestinationDir,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]$Channel = '',
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]$CargoProfile = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$RepoRoot = (Get-Item "$ScriptDir\..\.." | Select-Object -ExpandProperty FullName)
|
||||
$ResourcesSource = Join-Path $RepoRoot 'resources'
|
||||
|
||||
# Validate that the source resources directory exists
|
||||
if (-Not (Test-Path $ResourcesSource -PathType Container)) {
|
||||
Write-Error "Resources directory not found at $ResourcesSource"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create the destination directory if it doesn't exist
|
||||
if (-Not (Test-Path $DestinationDir)) {
|
||||
New-Item -ItemType Directory -Path $DestinationDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Copy bundled resources
|
||||
$BundledSource = Join-Path $ResourcesSource 'bundled'
|
||||
if (Test-Path $BundledSource -PathType Container) {
|
||||
$BundledDestination = Join-Path $DestinationDir 'bundled'
|
||||
Write-Output "Copying bundled resources to $BundledDestination"
|
||||
if (Test-Path $BundledDestination -PathType Container) {
|
||||
Remove-Item -Path $BundledDestination -Recurse -Force
|
||||
}
|
||||
Copy-Item -Path $BundledSource -Destination $BundledDestination -Recurse -Force
|
||||
} else {
|
||||
Write-Warning "No bundled directory found at $BundledSource"
|
||||
}
|
||||
|
||||
if ($env:GIT_RELEASE_TAG) {
|
||||
$VersionMetadataDir = Join-Path (Join-Path $DestinationDir 'bundled') 'metadata'
|
||||
$VersionMetadataPath = Join-Path $VersionMetadataDir 'version.json'
|
||||
Write-Output "Writing bundled Warp version metadata to $VersionMetadataPath"
|
||||
if (-Not (Test-Path $VersionMetadataDir -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $VersionMetadataDir -Force | Out-Null
|
||||
}
|
||||
|
||||
@{ warp_version = $env:GIT_RELEASE_TAG } |
|
||||
ConvertTo-Json |
|
||||
Set-Content -Path $VersionMetadataPath -Encoding utf8
|
||||
}
|
||||
|
||||
# Copy channel-gated skills matching the current release channel.
|
||||
$GatedSource = Join-Path (Join-Path $RepoRoot 'resources') 'channel-gated-skills'
|
||||
$DestSkills = Join-Path (Join-Path $DestinationDir 'bundled') 'skills'
|
||||
|
||||
if ($Channel -and (Test-Path $GatedSource -PathType Container)) {
|
||||
Write-Output "Copying channel-gated skills for channel '$Channel'..."
|
||||
|
||||
# Error out if a stable/ gate directory exists.
|
||||
$StableDir = Join-Path $GatedSource 'stable'
|
||||
if (Test-Path $StableDir -PathType Container) {
|
||||
Write-Error "Found a 'stable/' directory in $GatedSource. The stable channel does not use gated skills. Move stable-ready skills to resources/skills/ instead."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Gate labels ordered from most-inclusive to least-inclusive.
|
||||
$GateOrder = @('dogfood', 'preview')
|
||||
|
||||
# Map the release channel to its gate label.
|
||||
switch ($Channel) {
|
||||
'local' { $Gate = 'dogfood' }
|
||||
'dev' { $Gate = 'dogfood' }
|
||||
'preview' { $Gate = 'preview' }
|
||||
default {
|
||||
Write-Output " Channel '$Channel' has no gated skills, skipping"
|
||||
$Gate = $null
|
||||
}
|
||||
}
|
||||
|
||||
if ($Gate) {
|
||||
# Build the set of included gates (progressive: this gate and all after it).
|
||||
$GateIndex = [array]::IndexOf($GateOrder, $Gate)
|
||||
$IncludedGates = $GateOrder[$GateIndex..($GateOrder.Length - 1)]
|
||||
|
||||
foreach ($GateDir in Get-ChildItem -Path $GatedSource -Directory | Sort-Object Name) {
|
||||
if ($GateDir.Name -notin $IncludedGates) {
|
||||
$Skills = (Get-ChildItem -Path $GateDir.FullName -Directory | Sort-Object Name | ForEach-Object { $_.Name }) -join ', '
|
||||
Write-Output " Skipping gate '$($GateDir.Name)' (channel '$Channel') - would include: $Skills"
|
||||
continue
|
||||
}
|
||||
|
||||
foreach ($SkillDir in Get-ChildItem -Path $GateDir.FullName -Directory | Sort-Object Name) {
|
||||
$Dest = Join-Path $DestSkills $SkillDir.Name
|
||||
Write-Output " Copying gated skill: $($SkillDir.Name) (gate: $($GateDir.Name))"
|
||||
Copy-Item -Path $SkillDir.FullName -Destination $Dest -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate third-party license attribution.
|
||||
#
|
||||
# Additional (non-Cargo) third-party license files to include in the output.
|
||||
# When adding a new third-party component to the bundle, add its license file
|
||||
# to the repo alongside the component and add an entry here.
|
||||
# Cross-platform components:
|
||||
$AdditionalLicenses = @(
|
||||
@{ Name = 'Hack Font'; License = 'MIT'; Path = 'app\assets\bundled\fonts\hack\LICENSE.md' },
|
||||
@{ Name = 'Roboto Font'; License = 'SIL Open Font License'; Path = 'app\assets\bundled\fonts\roboto\LICENSE.txt' },
|
||||
@{ Name = 'bash-preexec'; License = 'MIT'; Path = 'app\assets\bundled\bootstrap\bash-preexec-LICENSE.md' },
|
||||
@{ Name = 'Claude API Skill'; License = 'Apache-2.0'; Path = 'resources\bundled\skills\claude-api\LICENSE.txt' },
|
||||
@{ Name = 'rudder-sdk-rust'; License = 'MIT'; Path = 'app\src\server\telemetry\LICENSE-RUDDER-SDK-RUST.txt' },
|
||||
@{ Name = 'Windows Terminal'; License = 'MIT'; Path = 'app\assets\windows\LICENSE-WINDOWS-TERMINAL' },
|
||||
@{ Name = 'GitHub Desktop'; License = 'MIT'; Path = 'app\src\code_review\GITHUB-DESKTOP-LICENSE' }
|
||||
)
|
||||
# Windows-only components:
|
||||
$AdditionalLicenses += @(
|
||||
@{ Name = 'OpenConsole / ConPTY (Windows Terminal)'; License = 'MIT'; Path = 'app\assets\windows\LICENSE-WINDOWS-TERMINAL' },
|
||||
@{ Name = 'DirectX Shader Compiler'; License = 'NCSA'; Path = 'app\assets\windows\LICENSE-DXC' }
|
||||
)
|
||||
|
||||
$LicensesOutput = Join-Path $DestinationDir 'THIRD_PARTY_LICENSES.txt'
|
||||
Write-Output "Generating third-party licenses at $LicensesOutput"
|
||||
cargo about generate --workspace --manifest-path "$RepoRoot\Cargo.toml" -c "$RepoRoot\about.toml" -o "$LicensesOutput" "$RepoRoot\about.hbs"
|
||||
if (-Not $?) {
|
||||
Write-Error 'Failed to generate third-party licenses'
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Append additional (non-Cargo) third-party licenses.
|
||||
foreach ($entry in $AdditionalLicenses) {
|
||||
$LicenseFile = Join-Path $RepoRoot $entry.Path
|
||||
if (-Not (Test-Path $LicenseFile)) {
|
||||
Write-Error "License file not found: $LicenseFile"
|
||||
exit 1
|
||||
}
|
||||
Add-Content -Path $LicensesOutput -Value ''
|
||||
Add-Content -Path $LicensesOutput -Value "$($entry.Name) ($($entry.License))"
|
||||
Add-Content -Path $LicensesOutput -Value ('-' * 80)
|
||||
Get-Content -Path $LicenseFile | Add-Content -Path $LicensesOutput
|
||||
Add-Content -Path $LicensesOutput -Value ''
|
||||
}
|
||||
|
||||
# Generate settings JSON schema unless explicitly skipped.
|
||||
if ($env:SKIP_SETTINGS_SCHEMA -ne '1') {
|
||||
$SchemaOutput = Join-Path $DestinationDir 'settings_schema.json'
|
||||
Write-Output "Generating settings schema at $SchemaOutput"
|
||||
|
||||
$SchemaCmd = @('run')
|
||||
if ($CargoProfile) {
|
||||
$SchemaCmd += @('--profile', $CargoProfile)
|
||||
}
|
||||
$SchemaCmd += @('--manifest-path', (Join-Path $RepoRoot 'Cargo.toml'), '--bin', 'generate_settings_schema', '--')
|
||||
if ($Channel) {
|
||||
$SchemaCmd += @('--channel', $Channel)
|
||||
}
|
||||
$SchemaCmd += $SchemaOutput
|
||||
|
||||
& cargo @SchemaCmd
|
||||
if (-Not $?) {
|
||||
Write-Error 'Failed to generate settings schema'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Successfully prepared bundled resources in $DestinationDir"
|
||||
@@ -0,0 +1,250 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
#include "environment.iss"
|
||||
|
||||
#define MyAppPublisher "Denver Technologies, Inc."
|
||||
#define MyAppURL "https://www.warp.dev/"
|
||||
#ifndef MyAppName
|
||||
#define MyAppName "WarpDev"
|
||||
#endif
|
||||
#ifndef MyAppVersion
|
||||
#define MyAppVersion "0.1.0"
|
||||
#endif
|
||||
#ifndef MyAppExeName
|
||||
#define MyAppExeName "dev.exe"
|
||||
#endif
|
||||
#ifndef ReleaseChannel
|
||||
#define ReleaseChannel "dev"
|
||||
#endif
|
||||
#ifndef TargetProfileDir
|
||||
#define TargetProfileDir "target\release-lto-debug_assertions"
|
||||
#endif
|
||||
#define AssetsDir "..\..\app\assets\windows"
|
||||
|
||||
// The mutex name must match what the Rust app creates in single_instance_manager.rs:
|
||||
#define ChannelPascalCase \
|
||||
(ReleaseChannel == "stable") ? "Stable" : \
|
||||
((ReleaseChannel == "dev") ? "Dev" : \
|
||||
((ReleaseChannel == "preview") ? "Preview" : \
|
||||
((ReleaseChannel == "local") ? "Local" : \
|
||||
((ReleaseChannel == "integration") ? "Integration" : \
|
||||
"Unknown"))))
|
||||
#define AppMutexName "Local\Warp" + ChannelPascalCase + "_SingleInstance"
|
||||
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId=warp-terminal-{#ReleaseChannel}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
UninstallDisplayName={#MyAppName}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
ArchitecturesAllowed={#Arch}
|
||||
ArchitecturesInstallIn64BitMode={#Arch}
|
||||
DisableProgramGroupPage=yes
|
||||
; The following line defaults the installer to use non administrative install mode (install for current user only).
|
||||
PrivilegesRequired=lowest
|
||||
; Allow the user to choose administrative install mode (install for all users).
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
OutputBaseFilename={#OutputName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
WizardSmallImageFile="installer-images\warp-logo.bmp"
|
||||
WizardImageFile="installer-images\warp-banner.bmp"
|
||||
SetupIconFile="..\..\app\channels\{#ReleaseChannel}\icon\no-padding\icon.ico"
|
||||
UninstallDisplayIcon="{app}\icon.ico"
|
||||
; Force close previous Warp if it hasn't shut down yet.
|
||||
; In the update flow we already warn the user if they have something running and make them confirm
|
||||
; before running this installer. Therefore, we are good to force close Warp without fear of losing
|
||||
; unsaved work.
|
||||
; VSCode does something similar:
|
||||
; https://github.com/microsoft/vscode/blob/aac9914f93551f894b8df1e4680bd847e7636be3/build/win32/code.iss#L41
|
||||
CloseApplications=force
|
||||
; For manual installs: if Warp is running, show a dialog prompting the user to close it
|
||||
; before Setup proceeds. Returned empty for background updates so the check is skipped.
|
||||
; TODO(andy) uncomment this after the 4/22 release
|
||||
;AppMutex={code:GetAppMutex}
|
||||
SetupMutex={#AppMutexName}Setup
|
||||
; Version 1809 / Build 18362 is required for ConPTY. See https://github.com/microsoft/vscode-docs/blob/9d736b662fdde3fed17d8bc2ed70bfea4ae20636/docs/supporting/troubleshoot-terminal-launch.md?plain=1#L66/
|
||||
MinVersion=10.0.18362
|
||||
; Tell Windows Explorer to reload the environment so that path changes take effect.
|
||||
ChangesEnvironment=true
|
||||
; Sign the setup engine and uninstaller so that the temporary bootstrapper
|
||||
; extracted to %TEMP% is Authenticode-signed. This prevents Microsoft Defender
|
||||
; ASR rule D4F940AB from blocking the installer in enterprise environments.
|
||||
; The sign tool command is supplied via ISCC /S on the command line; when not
|
||||
; defined (e.g. local dev builds) signing is skipped.
|
||||
#ifdef SIGN_TOOL
|
||||
SignTool=codesign
|
||||
SignedUninstaller=yes
|
||||
#endif
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
|
||||
[Files]
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
Source: "{#TargetProfileDir}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\conpty.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\OpenConsole.exe"; DestDir: "{app}\{#Arch}"; Flags: ignoreversion
|
||||
Source: "..\..\app\channels\{#ReleaseChannel}\icon\no-padding\icon.ico"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#AssetsDir}\{#Arch}\vcruntime140.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\vcruntime140_1.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\msvcp140.dll"; DestDir: "{app}"
|
||||
Source: "..\..\app\assets\bundled\bootstrap\pwsh.ps1"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\dxcompiler.dll"; DestDir: "{app}"
|
||||
Source: "{#AssetsDir}\{#Arch}\dxil.dll"; DestDir: "{app}"
|
||||
Source: "{#TargetProfileDir}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "SOFTWARE\Warp.dev\{#MyAppName}"; Flags: uninsdeletekey
|
||||
; cleanup "Open Warp Here" registry entries
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}"; Flags: deletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}"; Flags: deletekey
|
||||
; Add "Open Warp in new tab" to directory context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%1"""
|
||||
; Add "Open Warp in new tab" to directory background context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%V"""
|
||||
; Add "Open Warp in new window" to directory context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%1"""
|
||||
; Add "Open Warp in new window" to directory background context menu
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico"
|
||||
Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%V"""
|
||||
|
||||
[Tasks]
|
||||
Name: addToPath; Description: "Add Warp to PATH"
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{userappdata}\warp\{#MyAppName}"
|
||||
Type: filesandordirs; Name: "{localappdata}\warp\{#MyAppName}"
|
||||
Type: filesandordirs; Name: "{app}\bin"
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "dev.warp.{#MyAppName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "dev.warp.{#MyAppName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall runhidden nowait
|
||||
|
||||
[Code]
|
||||
function IsNotStable(): Boolean;
|
||||
begin
|
||||
#if ReleaseChannel == "stable"
|
||||
Result := False;
|
||||
#else
|
||||
Result := True;
|
||||
#endif
|
||||
end;
|
||||
|
||||
{ Returns true when the installer was launched by Warp's auto-update code.
|
||||
The auto-update path passes /update=1 on the command line and /NOCLOSEAPPLICATIONS
|
||||
so that the installer does not forcibly kill the running Warp process. Instead we
|
||||
wait for Warp to exit naturally by polling the app mutex below. }
|
||||
function IsBackgroundUpdate(): Boolean;
|
||||
begin
|
||||
Result := ExpandConstant('{param:update|false}') <> 'false';
|
||||
end;
|
||||
|
||||
{ For background updates, return an empty mutex name so that Inno Setup skips its
|
||||
built-in "application is running" dialog - we handle the wait ourselves. For manual
|
||||
installs, return the real mutex name so the user is prompted to close Warp first. }
|
||||
function GetAppMutex(Value: string): string;
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
Result := ''
|
||||
else
|
||||
Result := '{#AppMutexName}';
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
BinDir: string;
|
||||
CmdScriptName: string;
|
||||
CmdScriptPath: string;
|
||||
CmdScriptContent: string;
|
||||
WaitCounter: Integer;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
{ Background update: the installer was launched while Warp was still running.
|
||||
We passed /NOCLOSEAPPLICATIONS so Inno won't kill it. Wait here - before any
|
||||
files are touched - for Warp to release its single-instance mutex, which
|
||||
happens as part of normal process exit. }
|
||||
if CurStep = ssInstall then
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
begin
|
||||
Log('Background update: waiting for Warp to exit (mutex: {#AppMutexName})...');
|
||||
WaitCounter := 0;
|
||||
while CheckForMutexes('{#AppMutexName}') and (WaitCounter < 30) do
|
||||
begin
|
||||
Sleep(500);
|
||||
WaitCounter := WaitCounter + 1;
|
||||
end;
|
||||
if CheckForMutexes('{#AppMutexName}') then
|
||||
begin
|
||||
Log('Warp mutex still held after timeout; force-killing remaining processes.');
|
||||
{ Kill by image name. {#MyAppExeName} (e.g. warp.exe, dev.exe) is unique
|
||||
enough that collateral damage is not a concern. OpenConsole.exe is NOT
|
||||
killed by name because it is shared with Windows Terminal; instead we
|
||||
rely on Warp's Job Object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) to
|
||||
cascade-kill any child OpenConsole.exe processes when warp.exe dies. }
|
||||
Exec('taskkill.exe', '/f /im {#MyAppExeName}', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
if ResultCode <> 0 then
|
||||
Log('force-kill failed for {#MyAppExeName} (exit code: ' + IntToStr(ResultCode) + ')');
|
||||
Sleep(1000);
|
||||
end
|
||||
else
|
||||
Log('Warp has exited; proceeding with file installation.');
|
||||
end;
|
||||
end;
|
||||
|
||||
{ After a successful install, write a helper script for running the Warp CLI. }
|
||||
{ We use this to add a "warp-" prefix (e.g. "warp-preview.cmd" vs. "preview.exe") }
|
||||
if CurStep = ssPostInstall then begin
|
||||
{ Add Warp to PATH if requested }
|
||||
if IsTaskSelected('addToPath') then
|
||||
EnvAddPath(ExpandConstant('{app}\bin'));
|
||||
|
||||
BinDir := ExpandConstant('{app}\bin');
|
||||
if not DirExists(BinDir) then
|
||||
CreateDir(BinDir);
|
||||
|
||||
{ Determine the channel-specific script name. }
|
||||
#if ReleaseChannel == "stable"
|
||||
CmdScriptName := 'oz.cmd'
|
||||
#else
|
||||
CmdScriptName := 'oz-{#ReleaseChannel}.cmd';
|
||||
#endif
|
||||
|
||||
{ Create the helper CMD script }
|
||||
CmdScriptPath := BinDir + '\' + CmdScriptName;
|
||||
CmdScriptContent := '@echo off' + #13#10 +
|
||||
'set "WARP_CLI_MODE=1"' + #13#10 +
|
||||
'"' + ExpandConstant('{app}\{#MyAppExeName}') + '" %*' + #13#10;
|
||||
|
||||
SaveStringToFile(CmdScriptPath, CmdScriptContent, False);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
begin
|
||||
if CurUninstallStep = usPostUninstall then
|
||||
EnvRemovePath(ExpandConstant('{app}\bin'));
|
||||
end;
|
||||
Reference in New Issue
Block a user