Skip to content

Instantly share code, notes, and snippets.

@abdelouahabb
Last active August 4, 2026 09:53
Show Gist options
  • Select an option

  • Save abdelouahabb/9bd173e19461078c15daba6d78a4412c to your computer and use it in GitHub Desktop.

Select an option

Save abdelouahabb/9bd173e19461078c15daba6d78a4412c to your computer and use it in GitHub Desktop.
fixed Antigravity CLI installation
#!/usr/bin/env bash
# Antigravity Linux One-Command Wrapper & Installer
# Installs/updates Google Antigravity 2.0, Antigravity IDE, and CLI on Debian/Ubuntu.
set -euo pipefail
# 1. Local Workspace Setup
WORKSPACE="$HOME/ag_tmp"
mkdir -p "$WORKSPACE"
trap 'rm -rf "$WORKSPACE"' EXIT
# Self-referential URL setting for updater helper
INSTALLER_URL="${ANTIGRAVITY_LINUX_INSTALLER_URL:-https://opensnap.github.io/antigravity/install.sh}"
export ANTIGRAVITY_LINUX_INSTALLER_URL="$INSTALLER_URL"
ORIGINAL_ARGS=("$@")
PROJECT_NAME="antigravity-linux"
DOWNLOAD_PAGE="https://antigravity.google/download"
CLI_INSTALLER="https://antigravity.google/cli/install.sh"
INSTALL_DESKTOP=1
INSTALL_IDE=0
INSTALL_CLI=0
INSTALL_NAUTILUS=1
INSTALL_DEPS=1
DO_UNINSTALL=0
DO_STATUS=0
DO_PRINT_DOWNLOADS=0
FORCE=0
YES=0
log() { printf '🤖 %s\n' "$*"; }
warn() { printf '⚠️ WARN: %s\n' "$*" >&2; }
err() { printf '❌ ERROR: %s\n' "$*" >&2; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || err "Required command not found: $1"; }
# Redirected spinner output to stderr (>&2) to prevent command substitution pollution
spin() {
local pid=$1
local msg="$2"
local frames=("" "" "" "" "" "" "" "" "" "")
while kill -0 "$pid" 2>/dev/null; do
for frame in "${frames[@]}"; do
kill -0 "$pid" 2>/dev/null || break
printf "\r\033[K🤖 %s %s" "$frame" "$msg" >&2
sleep 0.1
done
done
wait "$pid"
if [ $? -eq 0 ]; then
printf "\r\033[K🤖 ✅ %s\n" "$msg" >&2
else
printf "\r\033[K❌ ERROR: %s failed.\n" "$msg" >&2
exit 1
fi
}
usage() {
cat <<'USAGE'
Antigravity Linux Installer
Usage:
install.sh [install|update] [options]
install.sh --status
install.sh --print-downloads
install.sh --uninstall
Options:
--desktop Install/update Antigravity 2.0 desktop app only
--ide Install/update Antigravity IDE only
--all Install/update Antigravity 2.0 desktop app + Antigravity IDE
--cli Also run Google's official Antigravity CLI installer
--no-nautilus Skip GNOME Files/Nautilus context-menu helper
--no-apt Do not install apt dependencies automatically
--force Reinstall even when the recorded version matches
--status Show installed helper-managed apps and versions
--print-downloads Print the resolved official Google tarball URLs
--uninstall Remove helper-managed Antigravity desktop/IDE files
-y, --yes Non-interactive mode
-h, --help Show this help
USAGE
}
# Parse Default or Provided Arguments
if [ $# -eq 0 ]; then
INSTALL_DESKTOP=1
INSTALL_IDE=1
fi
while [ $# -gt 0 ]; do
case "$1" in
install|update) ;;
--desktop) INSTALL_DESKTOP=1; INSTALL_IDE=0 ;;
--ide) INSTALL_DESKTOP=0; INSTALL_IDE=1 ;;
--all) INSTALL_DESKTOP=1; INSTALL_IDE=1 ;;
--cli) INSTALL_CLI=1 ;;
--no-nautilus) INSTALL_NAUTILUS=0 ;;
--no-apt) INSTALL_DEPS=0 ;;
--force) FORCE=1 ;;
--status) DO_STATUS=1 ;;
--print-downloads) DO_PRINT_DOWNLOADS=1 ;;
--uninstall) DO_UNINSTALL=1 ;;
-y|--yes) YES=1 ;;
-h|--help) usage; exit 0 ;;
*) err "Unknown option: $1" ;;
esac
shift
done
if [ "$(uname -s)" != "Linux" ]; then
err "This installer is for Linux only."
fi
case "$(uname -m)" in
x86_64|amd64) AG_PLATFORM="linux-x64"; DESKTOP_TOP="Antigravity-x64" ;;
aarch64|arm64) AG_PLATFORM="linux-arm"; DESKTOP_TOP="Antigravity-arm64" ;;
*) err "Unsupported CPU architecture: $(uname -m)." ;;
esac
require_root_or_reexec() {
if [ "$(id -u)" -eq 0 ]; then
return 0
fi
if command -v sudo >/dev/null 2>&1 && [ -n "${BASH_SOURCE[0]:-}" ] && [ -r "${BASH_SOURCE[0]}" ]; then
exec sudo -E env "ANTIGRAVITY_LINUX_INSTALLER_URL=$INSTALLER_URL" bash "${BASH_SOURCE[0]}" "${ORIGINAL_ARGS[@]}"
fi
err "System-wide install needs root privileges."
}
install_deps_debian() {
[ "$INSTALL_DEPS" -eq 1 ] || return 0
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
(
apt-get update -qq
local packages=(ca-certificates curl tar python3 desktop-file-utils xdg-utils)
if [ "$INSTALL_NAUTILUS" -eq 1 ] && [ "$INSTALL_IDE" -eq 1 ]; then
packages+=(python3-nautilus)
fi
apt-get install -y -qq "${packages[@]}"
) >/dev/null 2>&1 &
spin $! "Dependencies installed."
else
for c in curl tar python3; do need "$c"; done
fi
}
resolve_main_bundle() {
local tmpdir="$1"
local html="$tmpdir/download.html"
local js="$tmpdir/download.js"
(curl -fsSL --compressed --retry 3 -o "$html" "$DOWNLOAD_PAGE") >/dev/null 2>&1 &
spin $! "Fetching Google release endpoints..."
local main_js_url
main_js_url=$(python3 - "$html" "$DOWNLOAD_PAGE" <<'PY'
import re, sys
from pathlib import Path
from urllib.parse import urljoin
html = Path(sys.argv[1]).read_text(errors='replace')
page = sys.argv[2]
matches = re.findall(r'(?:src|href)=["\']([^"\']+\.js)["\']', html)
if not matches:
sys.exit(0)
print(urljoin(page, matches[-1]))
PY
)
if [ -n "$main_js_url" ]; then
curl -fsSL --compressed --retry 3 -o "$js" "$main_js_url" 2>/dev/null || echo "" > "$js"
else
echo "" > "$js"
fi
printf '%s\n' "$js"
}
resolve_download_from_bundle() {
local js="$1"
local product="$2"
python3 - "$js" "$AG_PLATFORM" "$product" <<'PY'
import html, re, sys
from pathlib import Path
from urllib.parse import unquote
bundle_path = Path(sys.argv[1])
html_path = bundle_path.parent / "download.html"
content = ""
if html_path.exists():
content += html_path.read_text(errors='ignore')
if bundle_path.exists():
content += bundle_path.read_text(errors='ignore')
content = html.unescape(content).replace('\\/', '/')
platform = sys.argv[2]
product = sys.argv[3]
def version_from_url(url):
decoded = unquote(url)
m = re.search(r'/(\d+\.\d+\.\d+-[0-9]+)/', decoded)
if not m:
m = re.search(r'(\d+\.\d+\.\d+)', decoded)
return m.group(1).split('-', 1)[0] if m else 'unknown'
if product == 'desktop':
pattern = r'https?://[^\'"\s<>)]*' + re.escape(platform) + r'/[^\'"\s<>)]*Antigravity\.tar\.gz'
else:
pattern = r'https?://[^\'"\s<>)]*' + re.escape(platform) + r'/[^\'"\s<>)]*Antigravity(?:%20|\+)IDE\.tar\.gz'
matches = re.findall(pattern, content, re.IGNORECASE)
if product == 'desktop':
matches = [u for u in matches if 'ide' not in u.lower()]
if matches:
url = matches[-1]
print(version_from_url(url), url)
sys.exit(0)
sys.stderr.write(f"⚠️ WARN: Could not dynamically resolve latest {product} link. Using fallback mirror.\n")
if product == 'desktop':
fallback_url = f"https://storage.googleapis.com/antigravity-public/antigravity-hub/2.4.3-5358163105546240/{platform}/Antigravity.tar.gz"
print("2.4.3", fallback_url)
else:
fallback_url = f"https://edgedl.me.gvt1.com/edgedl/release2/j0qc3/antigravity/stable/2.1.1-6123990880747520/{platform}/Antigravity%20IDE.tar.gz"
print("2.1.1", fallback_url)
PY
}
resolve_cli_version() {
local js="$1"
python3 - "$js" "$AG_PLATFORM" <<'PY'
import html, re, sys
from pathlib import Path
from urllib.parse import unquote
bundle_path = Path(sys.argv[1])
html_path = bundle_path.parent / "download.html"
content = ""
if html_path.exists():
content += html_path.read_text(errors='ignore')
if bundle_path.exists():
content += bundle_path.read_text(errors='ignore')
content = html.unescape(content).replace('\\/', '/')
platform = sys.argv[2]
def extract(pattern, text):
m = re.search(pattern, text, re.IGNORECASE)
return m.group(1) if m else None
# 1. Search for a direct agy tarball link in JS/HTML
m = re.search(r'https?://[^\'"\s<>)]*' + re.escape(platform) + r'/[^\'"\s<>)]*agy[^\'"\s<>)]*\.tar\.gz', content, re.IGNORECASE)
if m:
url = unquote(m.group(0))
v = extract(r'/(\d+\.\d+\.\d+-[0-9]+)/', url) or extract(r'(\d+\.\d+\.\d+)', url)
if v:
print(v.split('-', 1)[0])
sys.exit(0)
# 2. Search for generic JSON keys
v = extract(r'cli[_-]?version[\'"]?\s*:\s*[\'"]?(\d+\.\d+\.\d+)', content)
if v:
print(v)
sys.exit(0)
# 3. Last resort: fetch the install.sh and parse VERSION=
import urllib.request
try:
req = urllib.request.Request("https://antigravity.google/cli/install.sh", headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=5) as resp:
script_text = resp.read().decode('utf-8')
v = extract(r'VERSION=[\'"]?(\d+\.\d+\.\d+)[\'"]?', script_text)
if v:
print(v)
sys.exit(0)
except Exception:
pass
print("unknown")
PY
}
resolve_desktop_download() { resolve_download_from_bundle "$1" desktop; }
resolve_ide_download() { resolve_download_from_bundle "$1" ide; }
asar_extract_icon_png() {
local asar="$1"
local out="$2"
python3 - "$asar" "$out" <<'PY'
import json, struct, sys
from pathlib import Path
asar = Path(sys.argv[1])
out = Path(sys.argv[2])
with asar.open('rb') as f:
f.read(4)
header_size = struct.unpack('<I', f.read(4))[0]
f.read(4)
json_size = struct.unpack('<I', f.read(4))[0]
header = json.loads(f.read(json_size).decode())
icon = header.get('files', {}).get('icon.png')
if not icon:
raise SystemExit('icon.png not found')
with asar.open('rb') as f:
f.seek(8 + header_size + int(icon['offset']))
out.write_bytes(f.read(int(icon['size'])))
PY
}
safe_replace_dir() {
local newdir="$1"
local target="$2"
rm -rf "${target}.previous"
if [ -d "$target" ]; then
mv "$target" "${target}.previous"
fi
mv "$newdir" "$target"
}
fix_chrome_sandbox() {
local sandbox="$1"
if [ -f "$sandbox" ]; then
chown root:root "$sandbox"
chmod 4755 "$sandbox"
fi
}
refresh_desktop_caches() {
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database /usr/share/applications >/dev/null 2>&1 || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -q /usr/share/icons/hicolor >/dev/null 2>&1 || true
fi
}
installed_version() {
cat "$1" 2>/dev/null || true
}
install_desktop_app() {
local tmpdir="$1"
local js="$2"
local version url
read -r version url < <(resolve_desktop_download "$js")
local root="/opt/antigravity"
local target="$root/$DESKTOP_TOP/antigravity"
local version_file="$root/.antigravity-linux-version"
if [ "$FORCE" -eq 0 ] && [ -x "$target" ] && [ "$(installed_version "$version_file")" = "$version" ]; then
log "Antigravity 2.0 $version is already installed."
return
fi
local archive="$tmpdir/Antigravity.tar.gz"
(curl -fsSL --retry 3 -o "$archive" "$url") >/dev/null 2>&1 &
spin $! "Downloading Antigravity 2.0 ($version)..."
tar -tzf "$archive" > "$tmpdir/desktop-list.txt"
local top_dir
top_dir=$(sed -n '1{s#/.*##;p;q}' "$tmpdir/desktop-list.txt")
tar -xzf "$archive" -C "$tmpdir"
local icon_staged="$tmpdir/antigravity.png"
if [ -f "$tmpdir/$top_dir/resources/app.asar" ]; then
asar_extract_icon_png "$tmpdir/$top_dir/resources/app.asar" "$icon_staged" || warn "Could not extract desktop icon; continuing."
fi
rm -rf "${root}.new"
mkdir -p "${root}.new"
cp -a "$tmpdir/$top_dir" "${root}.new/"
printf '%s\n' "$version" > "${root}.new/.antigravity-linux-version"
printf '%s\n' "$url" > "${root}.new/.antigravity-linux-source-url"
fix_chrome_sandbox "${root}.new/$top_dir/chrome-sandbox"
safe_replace_dir "${root}.new" "$root"
ln -sfn "$root/$top_dir/antigravity" /usr/local/bin/antigravity
mkdir -p /usr/share/icons/hicolor/512x512/apps /usr/share/applications
if [ -f "$icon_staged" ]; then
install -m 0644 "$icon_staged" /usr/share/icons/hicolor/512x512/apps/antigravity.png
fi
cat > /usr/share/applications/antigravity.desktop <<DESKTOP
[Desktop Entry]
Name=Antigravity
Comment=Google Antigravity 2.0 agent platform
Exec=/usr/local/bin/antigravity %U
Icon=antigravity
Terminal=false
Type=Application
Categories=Development;IDE;
StartupNotify=true
StartupWMClass=Antigravity
DESKTOP
refresh_desktop_caches
log "Configured Antigravity 2.0 $version at $root/$top_dir"
}
install_ide_app() {
local tmpdir="$1"
local js="$2"
local version url
read -r version url < <(resolve_ide_download "$js")
local root="/opt/antigravity-ide"
local install_dir="Antigravity-IDE"
local version_file="$root/.antigravity-linux-version"
if [ "$FORCE" -eq 0 ] && [ -x "$root/$install_dir/antigravity-ide" ] && [ "$(installed_version "$version_file")" = "$version" ]; then
log "Antigravity IDE $version is already installed."
return
fi
local archive="$tmpdir/Antigravity-IDE.tar.gz"
(curl -fsSL --retry 3 -o "$archive" "$url") >/dev/null 2>&1 &
spin $! "Downloading Antigravity IDE ($version)..."
tar -tzf "$archive" > "$tmpdir/ide-list.txt"
local top_dir
top_dir=$(sed -n '1{s#/.*##;p;q}' "$tmpdir/ide-list.txt")
tar -xzf "$archive" -C "$tmpdir"
rm -rf "${root}.new"
mkdir -p "${root}.new/$install_dir"
cp -a "$tmpdir/$top_dir/." "${root}.new/$install_dir/"
printf '%s\n' "$version" > "${root}.new/.antigravity-linux-version"
printf '%s\n' "$url" > "${root}.new/.antigravity-linux-source-url"
fix_chrome_sandbox "${root}.new/$install_dir/chrome-sandbox"
safe_replace_dir "${root}.new" "$root"
ln -sfn "$root/$install_dir/antigravity-ide" /usr/local/bin/antigravity-ide
mkdir -p /usr/share/icons/hicolor/512x512/apps /usr/share/applications
local icon_source="$root/$install_dir/resources/app/resources/linux/code.png"
if [ -f "$icon_source" ]; then
install -m 0644 "$icon_source" /usr/share/icons/hicolor/512x512/apps/antigravity-ide.png
fi
cat > /usr/share/applications/antigravity-ide.desktop <<DESKTOP
[Desktop Entry]
Name=Antigravity IDE
Comment=Google Antigravity IDE
Exec=/usr/local/bin/antigravity-ide %F
Icon=antigravity-ide
Terminal=false
Type=Application
Categories=Development;IDE;
MimeType=inode/directory;text/plain;application/x-code-workspace;application/x-antigravity-workspace;x-scheme-handler/antigravity-ide;
StartupNotify=true
StartupWMClass=antigravity-ide
DESKTOP
refresh_desktop_caches
log "Configured Antigravity IDE $version at $root/$install_dir"
}
install_cli_app() {
local js="$1"
local target_user="${SUDO_USER:-$USER}"
local home_dir
home_dir=$(eval echo "~$target_user")
local cli_bin="$home_dir/.local/bin/agy"
local local_version="none"
if [ -x "$cli_bin" ]; then
local_version=$("$cli_bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || echo "none")
fi
local remote_version
remote_version=$(resolve_cli_version "$js")
if [ "$FORCE" -eq 0 ] && [ "$local_version" != "none" ] && [ "$local_version" = "$remote_version" ]; then
log "Antigravity CLI (agy) $local_version is already installed."
return
fi
if [ "$local_version" != "none" ]; then
if [ "$remote_version" != "unknown" ]; then
log "Updating Antigravity CLI from $local_version to $remote_version..."
else
log "Force updating Antigravity CLI (local: $local_version)..."
fi
# Delete binary to bypass Google's block message
rm -f "$cli_bin"
else
if [ "$remote_version" != "unknown" ]; then
log "Downloading Antigravity CLI ($remote_version)..."
else
log "Downloading Antigravity CLI..."
fi
fi
local cmd="curl -fsSL '$CLI_INSTALLER' | bash"
if [ "$target_user" != "root" ] && [ "$(id -u)" -eq 0 ]; then
(sudo -u "$target_user" -H bash -lc "$cmd") >/dev/null 2>&1 &
else
(eval "$cmd") >/dev/null 2>&1 &
fi
spin $! "Installing Antigravity CLI..."
if [ -x "$cli_bin" ]; then
local installed_ver
installed_ver=$("$cli_bin" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || echo "unknown")
log "Configured Antigravity CLI $installed_ver at $cli_bin"
fi
}
uninstall_all() {
require_root_or_reexec
rm -rf /opt/antigravity /opt/antigravity-ide
rm -f /usr/local/bin/antigravity /usr/local/bin/antigravity-ide /usr/local/bin/antigravity-linux
rm -f /usr/share/applications/antigravity.desktop /usr/share/applications/antigravity-ide.desktop
refresh_desktop_caches
log "Removed Antigravity components successfully."
}
main() {
if [ "$DO_STATUS" -eq 1 ]; then exit 0; fi
if [ "$DO_UNINSTALL" -eq 1 ]; then uninstall_all; exit 0; fi
require_root_or_reexec
install_deps_debian
local js
js=$(resolve_main_bundle "$WORKSPACE")
[ "$INSTALL_DESKTOP" -eq 1 ] && install_desktop_app "$WORKSPACE" "$js"
[ "$INSTALL_IDE" -eq 1 ] && install_ide_app "$WORKSPACE" "$js"
[ "$INSTALL_CLI" -eq 1 ] && install_cli_app "$js"
log "✅ Installation complete. You're ready to go!"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment