From 128ec80ed0753ad19b588dd813c9938ce71ddd26 Mon Sep 17 00:00:00 2001 From: delta-cloud-208e Date: Tue, 10 Mar 2026 17:45:41 +0000 Subject: [PATCH] fix(codex): macOS compatibility for install/update/uninstall scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace grep -oP (Perl regex) with portable sed — fixes "grep: invalid option -- P" on macOS - Add sedi() wrapper for cross-platform sed -i (BSD vs GNU) - Detect macOS via uname and use apple-darwin binary suffix instead of linux-musl - Add is_native_binary() helper: checks both ELF (Linux) and Mach-O (macOS) - macOS env vars: use launchctl setenv + /etc/codex-env.sh + ~/.zshrc source line - Linux env vars: keep /etc/environment + /etc/profile.d/ as before - Wrapper script uses dynamic ENV_FILE path instead of hardcoded /etc/profile.d/ - Fix SUDO_USER handling for correct ~/.zshrc path when run via sudo - Uninstaller: also remove .codex-bin, /etc/codex-env.sh, launchctl vars, rc file entries - Uninstaller: scan /Users/* on macOS instead of /home/* - Fix CRLF line endings in ucodex_uninstall.sh Co-Authored-By: Claude Opus 4.6 --- codex/ucodex_install.sh | 120 +++++++++++++++++----- codex/ucodex_uninstall.sh | 207 +++++++++++++++++++++++--------------- codex/ucodex_update.sh | 87 ++++++++++++---- codex/update-codex.sh | 2 +- 4 files changed, 287 insertions(+), 129 deletions(-) diff --git a/codex/ucodex_install.sh b/codex/ucodex_install.sh index 01c476f..55ce348 100755 --- a/codex/ucodex_install.sh +++ b/codex/ucodex_install.sh @@ -13,9 +13,32 @@ GITEA_TOKEN="${GITEA_TOKEN:-cadffcb0a6a3be728ac1ff619bb40c86588f6837}" REPO_RAW="https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/codex" GITHUB_API="https://api.github.com/repos/openai/codex/releases/latest" +OS="$(uname -s)" +IS_MACOS=false +[ "$OS" = "Darwin" ] && IS_MACOS=true + CODEX_BIN="/usr/local/bin/.codex-bin" CODEX_WRAPPER="/usr/local/bin/codex" -ENV_FILE="/etc/profile.d/codex-env.sh" +if $IS_MACOS; then + ENV_FILE="/etc/codex-env.sh" +else + ENV_FILE="/etc/profile.d/codex-env.sh" +fi + +# Cross-platform sed -i (macOS BSD sed requires -i '' while GNU sed uses -i) +sedi() { + if $IS_MACOS; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +# Cross-platform: check if file is a native binary (ELF on Linux, Mach-O on macOS) +is_native_binary() { + local f="$1" + [ -f "$f" ] && file "$f" | grep -qE "ELF|Mach-O" +} RED='\033[0;31m' GREEN='\033[0;32m' @@ -71,13 +94,21 @@ log "Python3 $(python3 --version | awk '{print $2}')" echo -e "\n${BOLD}Step 1: Installing Codex CLI binary...${NC}" ARCH=$(uname -m) -case "$ARCH" in - x86_64) BINARY_SUFFIX="x86_64-unknown-linux-musl" ;; - aarch64|arm64) BINARY_SUFFIX="aarch64-unknown-linux-musl" ;; - *) err "Unsupported architecture: $ARCH"; exit 1 ;; -esac +if $IS_MACOS; then + case "$ARCH" in + x86_64) BINARY_SUFFIX="x86_64-apple-darwin" ;; + arm64|aarch64) BINARY_SUFFIX="aarch64-apple-darwin" ;; + *) err "Unsupported architecture: $ARCH"; exit 1 ;; + esac +else + case "$ARCH" in + x86_64) BINARY_SUFFIX="x86_64-unknown-linux-musl" ;; + aarch64|arm64) BINARY_SUFFIX="aarch64-unknown-linux-musl" ;; + *) err "Unsupported architecture: $ARCH"; exit 1 ;; + esac +fi -LATEST_VER=$(curl -s "$GITHUB_API" | grep -oP '"tag_name":\s*"rust-v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) +LATEST_VER=$(curl -s "$GITHUB_API" | sed -n 's/.*"tag_name":[[:space:]]*"rust-v\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/p' | head -1) if [ -z "$LATEST_VER" ]; then err "Could not fetch latest version from GitHub" exit 1 @@ -85,7 +116,7 @@ fi info "Latest version: $LATEST_VER ($BINARY_SUFFIX)" # Migrate: if old codex is a real binary (not our wrapper), move it -if [ -f "$CODEX_WRAPPER" ] && file "$CODEX_WRAPPER" | grep -q "ELF"; then +if is_native_binary "$CODEX_WRAPPER"; then info "Migrating existing binary to $CODEX_BIN..." mv -f "$CODEX_WRAPPER" "$CODEX_BIN" fi @@ -125,7 +156,7 @@ else fi # Create temporary wrapper so patcher can find codex via `which codex` -if [ -f "$CODEX_BIN" ] && { [ ! -f "$CODEX_WRAPPER" ] || file "$CODEX_WRAPPER" | grep -q "ELF"; }; then +if [ -f "$CODEX_BIN" ] && { [ ! -f "$CODEX_WRAPPER" ] || is_native_binary "$CODEX_WRAPPER"; }; then cat > "$CODEX_WRAPPER" << 'TWEOF' #!/usr/bin/env bash exec /usr/local/bin/.codex-bin "$@" @@ -158,35 +189,70 @@ echo -e "\n${BOLD}Step 3: Setting environment variables...${NC}" API_KEY=$(python3 -c "import json; print(json.load(open('$INSTALL_DIR/codex_config.json'))['api_key'])") BASE_URL=$(python3 -c "import json; print(json.load(open('$INSTALL_DIR/codex_config.json'))['base_url'])") -# /etc/environment (all users, all sessions including cron) -ETC_ENV="/etc/environment" -for kv in "OPENAI_API_KEY=\"$API_KEY\"" "OPENAI_BASE_URL=\"${BASE_URL}/v1\""; do - KEY="${kv%%=*}" - if grep -q "^${KEY}=" "$ETC_ENV" 2>/dev/null; then - sed -i "s|^${KEY}=.*|${kv}|" "$ETC_ENV" - else - echo "$kv" >> "$ETC_ENV" - fi -done -log "Env vars written to $ETC_ENV" +if $IS_MACOS; then + # macOS: no /etc/environment, no /etc/profile.d + # Set launchctl env vars (GUI apps + new shells) + launchctl setenv OPENAI_API_KEY "$API_KEY" 2>/dev/null || true + launchctl setenv OPENAI_BASE_URL "${BASE_URL}/v1" 2>/dev/null || true + log "Env vars set via launchctl" -# /etc/profile.d/ (login shells) -cat > "$ENV_FILE" << ENVEOF + # Also write env file that wrapper will source + cat > "$ENV_FILE" << ENVEOF export OPENAI_API_KEY="$API_KEY" export OPENAI_BASE_URL="${BASE_URL}/v1" ENVEOF -chmod 644 "$ENV_FILE" -log "Env file: $ENV_FILE" + chmod 644 "$ENV_FILE" + log "Env file: $ENV_FILE" + + # Add to shell rc files for interactive shells (macOS default is zsh) + # When run via sudo, SUDO_USER has the real user; HOME may be /var/root + REAL_HOME="$HOME" + if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then + REAL_HOME=$(eval echo "~$SUDO_USER") + fi + for rc_file in "$REAL_HOME/.zshrc" "$REAL_HOME/.bashrc"; do + if [ -f "$rc_file" ] || [ "$rc_file" = "$REAL_HOME/.zshrc" ]; then + # Remove old entries first + if [ -f "$rc_file" ]; then + sedi '/# Codex env/d' "$rc_file" + sedi '/codex-env\.sh/d' "$rc_file" + fi + echo "[ -f $ENV_FILE ] && . $ENV_FILE # Codex env" >> "$rc_file" + log "Added source to $rc_file" + fi + done +else + # Linux: /etc/environment (all users, all sessions including cron) + ETC_ENV="/etc/environment" + for kv in "OPENAI_API_KEY=\"$API_KEY\"" "OPENAI_BASE_URL=\"${BASE_URL}/v1\""; do + KEY="${kv%%=*}" + if grep -q "^${KEY}=" "$ETC_ENV" 2>/dev/null; then + sedi "s|^${KEY}=.*|${kv}|" "$ETC_ENV" + else + echo "$kv" >> "$ETC_ENV" + fi + done + log "Env vars written to $ETC_ENV" + + # /etc/profile.d/ (login shells) + mkdir -p /etc/profile.d + cat > "$ENV_FILE" << ENVEOF +export OPENAI_API_KEY="$API_KEY" +export OPENAI_BASE_URL="${BASE_URL}/v1" +ENVEOF + chmod 644 "$ENV_FILE" + log "Env file: $ENV_FILE" +fi # ---- Step 4: Create wrapper (auto-loads env) ---- echo -e "\n${BOLD}Step 4: Creating wrapper...${NC}" -cat > "$CODEX_WRAPPER" << 'WRAPPER_EOF' +cat > "$CODEX_WRAPPER" << WRAPPER_EOF #!/usr/bin/env bash # Auto-generated wrapper — loads env vars before running codex binary -[ -f /etc/profile.d/codex-env.sh ] && . /etc/profile.d/codex-env.sh -exec /usr/local/bin/.codex-bin "$@" +[ -f ${ENV_FILE} ] && . ${ENV_FILE} +exec /usr/local/bin/.codex-bin "\$@" WRAPPER_EOF chmod +x "$CODEX_WRAPPER" log "Wrapper: $CODEX_WRAPPER -> $CODEX_BIN" diff --git a/codex/ucodex_uninstall.sh b/codex/ucodex_uninstall.sh index 2a881c1..b766c35 100755 --- a/codex/ucodex_uninstall.sh +++ b/codex/ucodex_uninstall.sh @@ -1,79 +1,128 @@ -#!/usr/bin/env bash -# Codex CLI — Uninstaller -# Removes Codex CLI binary, config, env vars. -# -# Usage: sudo bash ucodex_uninstall.sh -set -euo pipefail - -GREEN="\033[92m" -CYAN="\033[96m" -YELLOW="\033[93m" -BOLD="\033[1m" -RESET="\033[0m" - -log() { echo -e "${GREEN}[+]${RESET} $*"; } -warn() { echo -e "${YELLOW}[~]${RESET} $*"; } -info() { echo -e "${CYAN}[i]${RESET} $*"; } - -echo -e "${BOLD}" -echo " +--------------------------------------+" -echo " | Codex CLI — Uninstaller |" -echo " +--------------------------------------+" -echo -e "${RESET}" - -# ---- Remove binary ---- - -CODEX_PATH=$(which codex 2>/dev/null || echo "") -if [ -n "$CODEX_PATH" ]; then - info "Removing binary: $CODEX_PATH" - rm -f "$CODEX_PATH" - log "Binary removed" -else - warn "Codex binary not found in PATH" -fi - -# ---- Remove config ---- - -for user_home in /root /home/*; do - CODEX_DIR="$user_home/.codex" - if [ -d "$CODEX_DIR" ]; then - info "Removing $CODEX_DIR..." - rm -rf "$CODEX_DIR" - log "Removed $CODEX_DIR" - fi -done - -# ---- Remove env vars from shell rc files ---- - -for user_home in /root /home/*; do - for rc_file in "$user_home/.bashrc" "$user_home/.zshrc"; do - if [ -f "$rc_file" ] && grep -q 'OPENAI_API_KEY\|OPENAI_BASE_URL\|Codex' "$rc_file" 2>/dev/null; then - info "Cleaning env vars from $rc_file..." - sed -i '/# Codex/d' "$rc_file" - sed -i '/# UnlimitedCoding.*[Cc]odex/d' "$rc_file" - sed -i '/OPENAI_API_KEY/d' "$rc_file" - sed -i '/OPENAI_BASE_URL/d' "$rc_file" - log "Cleaned $rc_file" - fi - done -done - -# ---- Remove env vars from /etc/environment ---- - -if [ -f "/etc/environment" ] && grep -q 'OPENAI_API_KEY\|OPENAI_BASE_URL' /etc/environment 2>/dev/null; then - info "Cleaning /etc/environment..." - sed -i '/OPENAI_API_KEY/d' /etc/environment - sed -i '/OPENAI_BASE_URL/d' /etc/environment - log "Cleaned /etc/environment" -fi - -# ---- Remove /etc/profile.d script ---- - -if [ -f "/etc/profile.d/codex-cli.sh" ]; then - rm -f "/etc/profile.d/codex-cli.sh" - log "Removed /etc/profile.d/codex-cli.sh" -fi - -echo "" -echo -e "${GREEN}${BOLD} Codex CLI fully uninstalled!${RESET}" -echo "" +#!/usr/bin/env bash +# Codex CLI — Uninstaller +# Removes Codex CLI binary, config, env vars. +# +# Usage: sudo bash ucodex_uninstall.sh +set -euo pipefail + +OS="$(uname -s)" +IS_MACOS=false +[ "$OS" = "Darwin" ] && IS_MACOS=true + +# Cross-platform sed -i +sedi() { + if $IS_MACOS; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +GREEN="\033[92m" +CYAN="\033[96m" +YELLOW="\033[93m" +BOLD="\033[1m" +RESET="\033[0m" + +log() { echo -e "${GREEN}[+]${RESET} $*"; } +warn() { echo -e "${YELLOW}[~]${RESET} $*"; } +info() { echo -e "${CYAN}[i]${RESET} $*"; } + +echo -e "${BOLD}" +echo " +--------------------------------------+" +echo " | Codex CLI — Uninstaller |" +echo " +--------------------------------------+" +echo -e "${RESET}" + +# ---- Remove binary ---- + +CODEX_PATH=$(command -v codex 2>/dev/null || echo "") +if [ -n "$CODEX_PATH" ]; then + info "Removing wrapper: $CODEX_PATH" + rm -f "$CODEX_PATH" + log "Wrapper removed" +else + warn "Codex wrapper not found in PATH" +fi + +# Also remove the hidden binary +if [ -f "/usr/local/bin/.codex-bin" ]; then + info "Removing binary: /usr/local/bin/.codex-bin" + rm -f "/usr/local/bin/.codex-bin" + log "Binary removed" +fi + +# ---- Determine user home directories ---- + +if $IS_MACOS; then + _home_dirs=(/Users/*) +else + _home_dirs=(/root /home/*) +fi + +# ---- Remove config ---- + +for user_home in "${_home_dirs[@]}"; do + CODEX_DIR="$user_home/.codex" + if [ -d "$CODEX_DIR" ]; then + info "Removing $CODEX_DIR..." + rm -rf "$CODEX_DIR" + log "Removed $CODEX_DIR" + fi +done + +# ---- Remove env vars from shell rc files ---- + +for user_home in "${_home_dirs[@]}"; do + for rc_file in "$user_home/.bashrc" "$user_home/.zshrc"; do + if [ -f "$rc_file" ] && grep -q 'OPENAI_API_KEY\|OPENAI_BASE_URL\|Codex' "$rc_file" 2>/dev/null; then + info "Cleaning env vars from $rc_file..." + sedi '/# Codex/d' "$rc_file" + sedi '/# UnlimitedCoding.*[Cc]odex/d' "$rc_file" + sedi '/OPENAI_API_KEY/d' "$rc_file" + sedi '/OPENAI_BASE_URL/d' "$rc_file" + log "Cleaned $rc_file" + fi + done +done + +# ---- Remove env vars from /etc/environment ---- + +if [ -f "/etc/environment" ] && grep -q 'OPENAI_API_KEY\|OPENAI_BASE_URL' /etc/environment 2>/dev/null; then + info "Cleaning /etc/environment..." + sedi '/OPENAI_API_KEY/d' /etc/environment + sedi '/OPENAI_BASE_URL/d' /etc/environment + log "Cleaned /etc/environment" +fi + +# ---- Remove env file and profile.d scripts ---- + +for envf in "/etc/profile.d/codex-cli.sh" "/etc/profile.d/codex-env.sh" "/etc/codex-env.sh"; do + if [ -f "$envf" ]; then + rm -f "$envf" + log "Removed $envf" + fi +done + +# ---- macOS: unset launchctl env vars ---- + +if $IS_MACOS; then + launchctl unsetenv OPENAI_API_KEY 2>/dev/null || true + launchctl unsetenv OPENAI_BASE_URL 2>/dev/null || true + log "Unset launchctl env vars" +fi + +# ---- Clean codex-env.sh source lines from rc files ---- + +for user_home in "${_home_dirs[@]}"; do + for rc_file in "$user_home/.bashrc" "$user_home/.zshrc"; do + if [ -f "$rc_file" ] && grep -q 'codex-env\.sh' "$rc_file" 2>/dev/null; then + sedi '/codex-env\.sh/d' "$rc_file" + log "Cleaned codex-env source from $rc_file" + fi + done +done + +echo "" +echo -e "${GREEN}${BOLD} Codex CLI fully uninstalled!${RESET}" +echo "" diff --git a/codex/ucodex_update.sh b/codex/ucodex_update.sh index f1f292c..c4a63c1 100644 --- a/codex/ucodex_update.sh +++ b/codex/ucodex_update.sh @@ -10,9 +10,32 @@ GITEA_TOKEN="${GITEA_TOKEN:-cadffcb0a6a3be728ac1ff619bb40c86588f6837}" REPO_RAW="https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/codex" GITHUB_API="https://api.github.com/repos/openai/codex/releases/latest" +OS="$(uname -s)" +IS_MACOS=false +[ "$OS" = "Darwin" ] && IS_MACOS=true + CODEX_BIN="/usr/local/bin/.codex-bin" CODEX_WRAPPER="/usr/local/bin/codex" -ENV_FILE="/etc/profile.d/codex-env.sh" +if $IS_MACOS; then + ENV_FILE="/etc/codex-env.sh" +else + ENV_FILE="/etc/profile.d/codex-env.sh" +fi + +# Cross-platform sed -i +sedi() { + if $IS_MACOS; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +# Cross-platform: check if file is a native binary +is_native_binary() { + local f="$1" + [ -f "$f" ] && file "$f" | grep -qE "ELF|Mach-O" +} GREEN="\033[92m" CYAN="\033[96m" @@ -34,7 +57,7 @@ echo -e "${RESET}" # ---- Migrate old direct binary if needed ---- -if [ -f "$CODEX_WRAPPER" ] && file "$CODEX_WRAPPER" | grep -q "ELF"; then +if is_native_binary "$CODEX_WRAPPER"; then info "Migrating old binary to $CODEX_BIN..." mv -f "$CODEX_WRAPPER" "$CODEX_BIN" fi @@ -50,7 +73,7 @@ fi # ---- Get latest version ---- info "Checking latest version..." -LATEST_VER=$(curl -s "$GITHUB_API" | grep -oP '"tag_name":\s*"rust-v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) +LATEST_VER=$(curl -s "$GITHUB_API" | sed -n 's/.*"tag_name":[[:space:]]*"rust-v\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/p' | head -1) if [ -z "$LATEST_VER" ]; then err "Could not fetch latest version from GitHub" @@ -62,11 +85,19 @@ if [ "$OLD_VER" = "$LATEST_VER" ]; then log "Already up to date ($LATEST_VER)" else ARCH=$(uname -m) - case "$ARCH" in - x86_64) BINARY_SUFFIX="x86_64-unknown-linux-musl" ;; - aarch64|arm64) BINARY_SUFFIX="aarch64-unknown-linux-musl" ;; - *) err "Unsupported architecture: $ARCH"; exit 1 ;; - esac + if $IS_MACOS; then + case "$ARCH" in + x86_64) BINARY_SUFFIX="x86_64-apple-darwin" ;; + arm64|aarch64) BINARY_SUFFIX="aarch64-apple-darwin" ;; + *) err "Unsupported architecture: $ARCH"; exit 1 ;; + esac + else + case "$ARCH" in + x86_64) BINARY_SUFFIX="x86_64-unknown-linux-musl" ;; + aarch64|arm64) BINARY_SUFFIX="aarch64-unknown-linux-musl" ;; + *) err "Unsupported architecture: $ARCH"; exit 1 ;; + esac + fi DOWNLOAD_URL="https://github.com/openai/codex/releases/download/rust-v${LATEST_VER}/codex-${BINARY_SUFFIX}.tar.gz" TEMP_DIR=$(mktemp -d) @@ -110,28 +141,40 @@ python3 "$PATCH_DIR/codex_patcher.py" --apply --config "$PATCH_DIR/codex_config. API_KEY=$(python3 -c "import json; print(json.load(open('$PATCH_DIR/codex_config.json'))['api_key'])") BASE_URL=$(python3 -c "import json; print(json.load(open('$PATCH_DIR/codex_config.json'))['base_url'])") -ETC_ENV="/etc/environment" -for kv in "OPENAI_API_KEY=\"$API_KEY\"" "OPENAI_BASE_URL=\"${BASE_URL}/v1\""; do - KEY="${kv%%=*}" - if grep -q "^${KEY}=" "$ETC_ENV" 2>/dev/null; then - sed -i "s|^${KEY}=.*|${kv}|" "$ETC_ENV" - else - echo "$kv" >> "$ETC_ENV" - fi -done +if $IS_MACOS; then + launchctl setenv OPENAI_API_KEY "$API_KEY" 2>/dev/null || true + launchctl setenv OPENAI_BASE_URL "${BASE_URL}/v1" 2>/dev/null || true -cat > "$ENV_FILE" << ENVEOF + cat > "$ENV_FILE" << ENVEOF export OPENAI_API_KEY="$API_KEY" export OPENAI_BASE_URL="${BASE_URL}/v1" ENVEOF -chmod 644 "$ENV_FILE" + chmod 644 "$ENV_FILE" +else + ETC_ENV="/etc/environment" + for kv in "OPENAI_API_KEY=\"$API_KEY\"" "OPENAI_BASE_URL=\"${BASE_URL}/v1\""; do + KEY="${kv%%=*}" + if grep -q "^${KEY}=" "$ETC_ENV" 2>/dev/null; then + sedi "s|^${KEY}=.*|${kv}|" "$ETC_ENV" + else + echo "$kv" >> "$ETC_ENV" + fi + done + + mkdir -p /etc/profile.d + cat > "$ENV_FILE" << ENVEOF +export OPENAI_API_KEY="$API_KEY" +export OPENAI_BASE_URL="${BASE_URL}/v1" +ENVEOF + chmod 644 "$ENV_FILE" +fi # ---- Create/update wrapper ---- -cat > "$CODEX_WRAPPER" << 'WRAPPER_EOF' +cat > "$CODEX_WRAPPER" << WRAPPER_EOF #!/usr/bin/env bash -[ -f /etc/profile.d/codex-env.sh ] && . /etc/profile.d/codex-env.sh -exec /usr/local/bin/.codex-bin "$@" +[ -f ${ENV_FILE} ] && . ${ENV_FILE} +exec /usr/local/bin/.codex-bin "\$@" WRAPPER_EOF chmod +x "$CODEX_WRAPPER" diff --git a/codex/update-codex.sh b/codex/update-codex.sh index 66a0aac..5316f09 100755 --- a/codex/update-codex.sh +++ b/codex/update-codex.sh @@ -56,7 +56,7 @@ get_current_version() { # Функция получения последней версии с GitHub get_latest_version() { - curl -s "$GITHUB_API" | grep -oP '"tag_name":\s*"rust-v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 + curl -s "$GITHUB_API" | sed -n 's/.*"tag_name":[[:space:]]*"rust-v\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/p' | head -1 } # Функция сравнения версий (возвращает 0 если нужно обновление)