-
Notifications
You must be signed in to change notification settings - Fork 0
Add install.sh: automated macOS installer for Copilot for Xcode #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,160 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # install.sh — Install Copilot for Xcode on macOS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Usage: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # bash install.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The script will: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 1. Verify prerequisites (macOS 12+, Xcode installed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 2. Install the app via Homebrew cask when available, or download the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # latest release from GitHub otherwise | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 3. Launch the app once so it registers its background launch agent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 4. Print step-by-step instructions for the manual post-install tasks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (enabling the Source Editor Extension and granting Accessibility access) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── color helpers ───────────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RED='\033[0;31m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GREEN='\033[0;32m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| YELLOW='\033[1;33m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CYAN='\033[0;36m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BOLD='\033[1m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RESET='\033[0m' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| info() { echo -e "${CYAN}[info]${RESET} $*"; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success() { echo -e "${GREEN}[ok]${RESET} $*"; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| warn() { echo -e "${YELLOW}[warn]${RESET} $*"; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error() { echo -e "${RED}[error]${RESET} $*" >&2; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| step() { echo -e "\n${BOLD}$*${RESET}"; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── constants ───────────────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| APP_NAME="Copilot for Xcode" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| APP_BUNDLE="Copilot for Xcode.app" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| APP_PATH="/Applications/${APP_BUNDLE}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BREW_CASK="copilot-for-xcode" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GITHUB_RELEASES="https://api.github.com/repos/intitni/CopilotForXcode/releases/latest" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MIN_MACOS_MAJOR=12 # macOS Monterey | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── prerequisite checks ─────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| step "1/4 Checking prerequisites" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # macOS version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| os_version=$(sw_vers -productVersion 2>/dev/null || echo "0.0") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| os_major=$(echo "$os_version" | cut -d. -f1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$os_major" -lt "$MIN_MACOS_MAJOR" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error "${APP_NAME} requires macOS Monterey (12) or later." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error "Current version: ${os_version}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success "macOS ${os_version} — compatible" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Xcode installed (look for the app bundle or xcode-select path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! xcode-select -p &>/dev/null || [[ ! -d "$(xcode-select -p)" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error "Xcode does not appear to be installed." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error "Install Xcode from the Mac App Store, then re-run this script." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success "Xcode found at $(xcode-select -p)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+59
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Xcode installed (look for the app bundle or xcode-select path) | |
| if ! xcode-select -p &>/dev/null || [[ ! -d "$(xcode-select -p)" ]]; then | |
| error "Xcode does not appear to be installed." | |
| error "Install Xcode from the Mac App Store, then re-run this script." | |
| exit 1 | |
| fi | |
| success "Xcode found at $(xcode-select -p)" | |
| # Xcode installed (require full Xcode, not just Command Line Tools) | |
| xcode_path="$(xcode-select -p 2>/dev/null || true)" | |
| if [[ -z "$xcode_path" || ! -d "$xcode_path" ]]; then | |
| error "Xcode does not appear to be installed." | |
| error "Install Xcode from the Mac App Store, then re-run this script." | |
| exit 1 | |
| fi | |
| # Detect Command Line Tools-only setup and prefer the full Xcode.app if present | |
| if [[ "$xcode_path" == /Library/Developer/CommandLineTools* ]]; then | |
| if [[ -d "/Applications/Xcode.app/Contents/Developer" ]]; then | |
| xcode_path="/Applications/Xcode.app/Contents/Developer" | |
| else | |
| error "Only Xcode Command Line Tools were detected at: $xcode_path" | |
| error "${APP_NAME} requires the full Xcode.app (including Source Editor Extensions)." | |
| error "Install Xcode from the Mac App Store, then re-run this script." | |
| exit 1 | |
| fi | |
| fi | |
| success "Xcode found at $xcode_path" |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GitHub Releases lookup runs inside a command substitution with set -e + pipefail, so if the API call fails (rate limit/network, 403/404), the script will exit immediately without printing the friendly fallback errors below. Consider capturing the curl output/exit code explicitly (or retrying / surfacing the HTTP error) so users get a clear message and manual-download link instead of a silent abort.
| asset_url=$( | |
| curl -fsSL "$GITHUB_RELEASES" \ | |
| | grep '"browser_download_url"' \ | |
| | grep '\.zip"' \ | |
| | head -1 \ | |
| | sed 's/.*"browser_download_url": "\(.*\)"/\1/' | |
| # Fetch release JSON from GitHub, handling HTTP/network errors explicitly | |
| if ! release_json=$(curl -fsSL "$GITHUB_RELEASES"); then | |
| error "Failed to contact GitHub Releases API." | |
| error "Please download ${APP_NAME} manually from:" | |
| error " https://github.com/intitni/CopilotForXcode/releases" | |
| exit 1 | |
| fi | |
| # Extract the first .zip asset URL from the release JSON | |
| asset_url=$( | |
| printf '%s\n' "$release_json" \ | |
| | grep '"browser_download_url"' \ | |
| | grep '\.zip"' \ | |
| | head -1 \ | |
| | sed 's/.*"browser_download_url": "\(.*\)"/\1/' \ | |
| || true |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The temp directory is only cleaned up on the success path and a couple of specific error branches. If curl, unzip, or the copy step fails, tmp_dir will be left behind. Consider using a trap to ensure cleanup on any exit (success or failure).
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find ... -name "*.app" | head -1 can select the wrong bundle if the extracted archive contains multiple .apps (e.g., helper apps inside the main app bundle). Prefer selecting the expected bundle name (e.g., ${APP_BUNDLE}) and/or limiting the search depth so you always install the host app.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copying/removing under /Applications often requires admin privileges. As written, rm -rf / cp -R will fail with a permission error for many users (and set -e will exit without guidance). Consider detecting writability of /Applications (or $APP_PATH) and using sudo when needed, with a clear prompt/message.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
os_minor is computed but never used. Please remove it (or use it) to avoid dead code / confusion.
| os_minor=$(echo "$os_version" | cut -d. -f2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The one-liner installs by executing
install.shfetched from themainbranch viaraw.githubusercontent.com, not a locally "bundled" script. Consider rewording to avoid implying it runs a script shipped with a downloaded release/clone, and (optionally) note that it always pulls the latest script frommainrather than a pinned release for reproducibility.