Skip to content

Instantly share code, notes, and snippets.

@ben-vargas
Created September 23, 2025 16:18
Show Gist options
  • Select an option

  • Save ben-vargas/cf8d00c8521bd2bdcf94150095c23004 to your computer and use it in GitHub Desktop.

Select an option

Save ben-vargas/cf8d00c8521bd2bdcf94150095c23004 to your computer and use it in GitHub Desktop.
Codex Binary Install Script
#!/usr/bin/env bash
set -euo pipefail
# ==============================
# Codex Installer Script
# Repository: https://github.com/openai/codex
# ==============================
# Config (overridable via env or flags)
REPO="${REPO:-openai/codex}"
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
PREFERRED_LINUX_LIBC="${PREFERRED_LINUX_LIBC:-musl}" # musl | gnu
# Flags: -d|--install-dir DIR, --prefer-libc musl|gnu
while [ $# -gt 0 ]; do
case "$1" in
-d|--install-dir) INSTALL_DIR="$2"; shift 2;;
--prefer-libc) PREFERRED_LINUX_LIBC="$2"; shift 2;;
-h|--help)
cat <<EOF
Codex Installer - Install the OpenAI Codex terminal coding agent
Usage: $0 [options]
Options:
-d, --install-dir DIR Installation directory (default: ~/.local/bin)
--prefer-libc musl|gnu Linux libc preference (default: musl)
-h, --help Show this help message
Environment Variables:
GITHUB_TOKEN Set to increase API rate limit (optional)
INSTALL_DIR Override default installation directory
PREFERRED_LINUX_LIBC Set Linux libc preference (musl or gnu)
Examples:
$0 # Install latest version to ~/.local/bin
$0 -d /usr/local/bin # Install to /usr/local/bin
$0 --prefer-libc gnu # Prefer GNU libc on Linux
EOF
exit 0;;
*) echo "Unknown option: $1" >&2; exit 1;;
esac
done
# ==============================
# Helper Functions
# ==============================
have() { command -v "$1" >/dev/null 2>&1; }
api_get() {
local url="$1"
local headers=(-H "Accept: application/vnd.github+json"
-H "X-GitHub-Api-Version: 2022-11-28"
-H "User-Agent: codex-installer")
[ -n "${GITHUB_TOKEN:-}" ] && headers+=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
curl -fsSL "${headers[@]}" "$url"
}
# JSON parsing helper: prefer jq, fallback to python3
json_q() {
local filter="$1"
if have jq; then jq -r "$filter"
elif have python3; then
python3 - "$filter" <<'PY'
import json, sys
flt = sys.argv[1]
data = json.load(sys.stdin)
def non_draft(items): return [x for x in items if not x.get("draft")]
def field(items, key): return [x.get(key, "") for x in items]
def assets(items, idx): return items[idx].get("assets", [])
def names(assets): return [a.get("name","") for a in assets]
def url_for(assets, name):
for a in assets:
if a.get("name")==name:
return a.get("browser_download_url","")
return ""
if flt == 'tags':
for t in field(non_draft(data), "tag_name"): print(t)
elif flt == 'dates':
for d in field(non_draft(data), "published_at"):
print((d or "").split("T")[0])
elif flt == 'pre':
for p in field(non_draft(data), "prerelease"):
print("true" if p else "false")
elif flt.startswith('asset-names:'):
idx = int(flt.split(':',1)[1])
for n in names(assets(non_draft(data), idx)): print(n)
elif flt.startswith('asset-url:'):
_, idx, name = flt.split(':',2)
idx = int(idx)
print(url_for(assets(non_draft(data), idx), name))
PY
else
echo "Error: need 'jq' or 'python3' to parse GitHub JSON." >&2
exit 1
fi
}
# ==============================
# Detect OS/Architecture
# ==============================
OS="$(uname -s)"
ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
x86_64|amd64) ARCH="x86_64";;
arm64|aarch64) ARCH="aarch64";;
*) echo "Unsupported architecture: $ARCH_RAW" >&2; exit 1;;
esac
case "$OS" in
Linux) PLATFORM="unknown-linux";;
Darwin) PLATFORM="apple-darwin";;
*) echo "Unsupported OS: $OS" >&2; exit 1;;
esac
# Build asset candidates in order of preference
CANDIDATES=()
if [ "$OS" = "Linux" ]; then
if [ "$PREFERRED_LINUX_LIBC" = "gnu" ]; then
CANDIDATES+=("codex-${ARCH}-${PLATFORM}-gnu.tar.gz" "codex-${ARCH}-${PLATFORM}-musl.tar.gz")
else
CANDIDATES+=("codex-${ARCH}-${PLATFORM}-musl.tar.gz" "codex-${ARCH}-${PLATFORM}-gnu.tar.gz")
fi
else # macOS
CANDIDATES+=("codex-${ARCH}-${PLATFORM}.tar.gz")
fi
# ==============================
# Fetch Releases
# ==============================
API_URL="https://api.github.com/repos/${REPO}/releases?per_page=10"
printf "🚀 Codex Installer\n"
printf "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
printf "Repository: %s\n" "https://github.com/$REPO"
printf "Platform: %s (%s)\n" "$OS" "$ARCH"
printf "Install to: %s\n" "$INSTALL_DIR"
printf "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
echo "📦 Fetching available releases..."
JSON="$(api_get "$API_URL")" || {
echo "❌ Failed to fetch releases from GitHub"
echo " This might be due to rate limiting. Try setting GITHUB_TOKEN environment variable."
exit 1
}
# Parse release data
if have jq; then
mapfile -t TAGS < <(printf '%s' "$JSON" | jq -r 'map(select(.draft==false)) | .[].tag_name')
mapfile -t DATES < <(printf '%s' "$JSON" | jq -r 'map(select(.draft==false)) | .[].published_at | split("T")[0]')
mapfile -t PRE < <(printf '%s' "$JSON" | jq -r 'map(select(.draft==false)) | .[].prerelease')
else
mapfile -t TAGS < <(printf '%s' "$JSON" | json_q 'tags')
mapfile -t DATES < <(printf '%s' "$JSON" | json_q 'dates')
mapfile -t PRE < <(printf '%s' "$JSON" | json_q 'pre')
fi
COUNT="${#TAGS[@]}"
[ "$COUNT" -eq 0 ] && { echo "❌ No releases found."; exit 1; }
[ "$COUNT" -gt 10 ] && COUNT=10
# Build choice menu
CHOICES=()
for ((i=0; i<COUNT; i++)); do
label="${TAGS[$i]} — ${DATES[$i]:-}"
[ "$i" -eq 0 ] && label="$label ✨ latest"
[ "${PRE[$i]}" = "true" ] && label="$label ⚠️ pre-release"
CHOICES+=("$label")
done
# ==============================
# Interactive Selection
# ==============================
echo
if have fzf; then
echo "📋 Select a version (use arrow keys or type to filter):"
SEL_LINE=$(printf '%s\n' "${CHOICES[@]}" | fzf --prompt="Version ▶ " \
--header="↑/↓ navigate • Enter to select • Ctrl-C to cancel" \
--border --height=50% --reverse --no-multi \
--color=prompt:cyan,header:gray,border:gray \
|| true)
if [ -z "${SEL_LINE:-}" ]; then
echo "Installation cancelled."
exit 0
fi
# Map selection back to index
INDEX=0
for i in "${!CHOICES[@]}"; do
if [ "${CHOICES[$i]}" = "$SEL_LINE" ]; then INDEX="$i"; break; fi
done
else
echo "📋 Available versions:"
for i in "${!CHOICES[@]}"; do
printf " %2d) %s\n" "$((i+1))" "${CHOICES[$i]}"
done
echo
read -r -p "Select version number [1]: " NUM
NUM="${NUM:-1}"
[[ "$NUM" =~ ^[0-9]+$ ]] && [ "$NUM" -ge 1 ] && [ "$NUM" -le "$COUNT" ] || {
echo "❌ Invalid selection."
exit 1
}
INDEX=$((NUM-1))
fi
TAG="${TAGS[$INDEX]}"
echo
echo "✅ Selected: ${TAG}"
# ==============================
# Find Compatible Asset
# ==============================
echo "🔍 Finding compatible binary..."
if have jq; then
ASSET_NAMES="$(printf '%s' "$JSON" | jq -r "map(select(.draft==false)) | .[$INDEX].assets | .[].name")"
else
ASSET_NAMES="$(printf '%s' "$JSON" | json_q "asset-names:$INDEX")"
fi
ASSET_NAME=""
ASSET_URL=""
for cand in "${CANDIDATES[@]}"; do
if printf '%s\n' "$ASSET_NAMES" | grep -qx "$cand"; then
ASSET_NAME="$cand"
if have jq; then
ASSET_URL="$(printf '%s' "$JSON" | jq -r \
"map(select(.draft==false)) | .[$INDEX].assets | .[] | select(.name==\"$ASSET_NAME\") | .browser_download_url" | head -n1)"
else
ASSET_URL="$(printf '%s' "$JSON" | json_q "asset-url:$INDEX:$ASSET_NAME")"
fi
break
fi
done
if [ -z "$ASSET_URL" ]; then
echo "❌ No compatible binary found for your system."
echo " OS: $OS, Architecture: $ARCH"
echo
echo "Available assets for this release:"
printf ' • %s\n' $ASSET_NAMES
exit 1
fi
echo "✅ Found: $ASSET_NAME"
# ==============================
# Download and Install
# ==============================
TMPDIR="$(mktemp -d)"
trap "rm -rf $TMPDIR" EXIT
TARBALL="$TMPDIR/$ASSET_NAME"
echo "⬇️ Downloading..."
curl -fL --progress-bar -o "$TARBALL" "$ASSET_URL" || {
echo "❌ Download failed"
exit 1
}
echo "📦 Extracting..."
tar -xzf "$TARBALL" -C "$TMPDIR" || {
echo "❌ Extraction failed"
exit 1
}
# Find the binary
BIN_SRC="$(find "$TMPDIR" -maxdepth 1 -type f -name 'codex*' ! -name '*.tar.gz' ! -name '*.zst' | head -n1)"
[ -n "$BIN_SRC" ] || {
echo "❌ Could not locate codex binary in archive"
exit 1
}
# Create install directory and backup existing binary
mkdir -p "$INSTALL_DIR"
BIN_DST="$INSTALL_DIR/codex"
if [ -f "$BIN_DST" ]; then
BACKUP="$BIN_DST.bak.$(date +%Y%m%d%H%M%S)"
cp -f "$BIN_DST" "$BACKUP"
echo "📋 Backed up existing binary to: $BACKUP"
fi
# Install the binary
install -m 0755 "$BIN_SRC" "$BIN_DST" || {
echo "❌ Installation failed"
exit 1
}
# ==============================
# Verify Installation
# ==============================
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Installation complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo
echo "📍 Installed to: $BIN_DST"
# Check version
if "$BIN_DST" --version 2>/dev/null; then
:
else
echo " Version check failed (binary might still work)"
fi
# PATH check
echo
case ":$PATH:" in
*":$INSTALL_DIR:"*)
echo "✅ $INSTALL_DIR is already in your PATH"
echo
echo "🎉 You can now run: codex"
;;
*)
echo "⚠️ $INSTALL_DIR is not in your PATH"
echo
echo "To use codex, add this line to your shell config:"
echo
echo " For bash (~/.bashrc):"
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
echo
echo " For zsh (~/.zshrc):"
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
echo
echo "Then reload your shell or run:"
echo " source ~/.bashrc # or ~/.zshrc"
;;
esac
echo
echo "📚 Documentation: https://github.com/openai/codex"
echo "🐛 Report issues: https://github.com/openai/codex/issues"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment