chore: import upstream snapshot with attribution
This commit is contained in:
Executable
+326
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env bash
|
||||
# Reset CodexBar: kill running instances, build, package, relaunch, verify.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP_BUNDLE="${ROOT_DIR}/CodexBar.app"
|
||||
APP_PROCESS_PATTERN="CodexBar.app/Contents/MacOS/CodexBar"
|
||||
DEBUG_PROCESS_PATTERN="${ROOT_DIR}/.build/debug/CodexBar"
|
||||
RELEASE_PROCESS_PATTERN="${ROOT_DIR}/.build/release/CodexBar"
|
||||
LOCK_KEY="$(printf '%s' "${ROOT_DIR}" | shasum -a 256 | cut -c1-8)"
|
||||
LOCK_DIR="${TMPDIR:-/tmp}/codexbar-compile-and-run-${LOCK_KEY}"
|
||||
LOCK_PID_FILE="${LOCK_DIR}/pid"
|
||||
WAIT_FOR_LOCK=0
|
||||
RUN_TESTS=0
|
||||
DEBUG_LLDB=0
|
||||
RELEASE_ARCHES=""
|
||||
SIGNING_MODE="${CODEXBAR_SIGNING:-}"
|
||||
CLEAR_ADHOC_KEYCHAIN=0
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
delete_keychain_service_items() {
|
||||
local service="$1"
|
||||
security delete-generic-password -s "${service}" >/dev/null 2>&1 || true
|
||||
while security delete-generic-password -s "${service}" >/dev/null 2>&1; do
|
||||
:
|
||||
done
|
||||
}
|
||||
|
||||
# Ensure Swift >= 5.5 (required for --arch flag in swift build)
|
||||
ensure_swift_version() {
|
||||
local swift_output
|
||||
local swift_ver
|
||||
swift_output=$(swift --version 2>&1 || true)
|
||||
if [[ "$swift_output" =~ (Apple[[:space:]]+)?Swift[[:space:]]+version[[:space:]]+([0-9]+)\.([0-9]+)(\.[0-9]+)? ]]; then
|
||||
swift_ver="${BASH_REMATCH[2]}.${BASH_REMATCH[3]}${BASH_REMATCH[4]}"
|
||||
else
|
||||
fail "Swift >= 5.5 required (found ${swift_output:-none}). Install Xcode or update swiftly."
|
||||
fi
|
||||
local major minor
|
||||
major=$(echo "$swift_ver" | cut -d. -f1)
|
||||
minor=$(echo "$swift_ver" | cut -d. -f2)
|
||||
if [[ "${major:-0}" -ge 6 ]] || { [[ "${major:-0}" -eq 5 ]] && [[ "${minor:-0}" -ge 5 ]]; }; then
|
||||
return 0
|
||||
fi
|
||||
# Try Xcode toolchain
|
||||
local xcrun_swift
|
||||
xcrun_swift=$(xcrun --find swift 2>/dev/null || true)
|
||||
if [[ -n "$xcrun_swift" && -x "$xcrun_swift" ]]; then
|
||||
log "WARN: PATH swift is v${swift_ver}; switching to Xcode toolchain at $(dirname "$xcrun_swift")"
|
||||
export PATH="$(dirname "$xcrun_swift"):$PATH"
|
||||
return 0
|
||||
fi
|
||||
fail "Swift >= 5.5 required (found ${swift_ver:-none}). Install Xcode or update swiftly."
|
||||
}
|
||||
|
||||
has_signing_identity() {
|
||||
local identity="${1:-}"
|
||||
if [[ -z "${identity}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
security find-identity -p codesigning -v 2>/dev/null | grep -F "${identity}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
detect_codesigning_identity() {
|
||||
local preferred_prefixes=(
|
||||
"Developer ID Application:"
|
||||
"Apple Development:"
|
||||
"Apple Distribution:"
|
||||
)
|
||||
local prefix
|
||||
local identities
|
||||
identities="$(security find-identity -p codesigning -v 2>/dev/null || true)"
|
||||
for prefix in "${preferred_prefixes[@]}"; do
|
||||
awk -v prefix="${prefix}" '
|
||||
index($0, "\"" prefix) {
|
||||
sub(/^[^\"]*\"/, "")
|
||||
sub(/\".*$/, "")
|
||||
print
|
||||
exit
|
||||
}
|
||||
' <<<"${identities}"
|
||||
done | sed -n '1p'
|
||||
}
|
||||
|
||||
export_team_id_from_identity() {
|
||||
local identity="${1:-}"
|
||||
if [[ -n "${APP_TEAM_ID:-}" || -z "${identity}" ]]; then
|
||||
return
|
||||
fi
|
||||
local subject
|
||||
subject="$(security find-certificate -c "${identity}" -p 2>/dev/null \
|
||||
| openssl x509 -noout -subject -nameopt RFC2253 2>/dev/null || true)"
|
||||
if [[ "${subject}" =~ (^|,)OU=([A-Z0-9]{10})(,|$) ]]; then
|
||||
APP_TEAM_ID="${BASH_REMATCH[2]}"
|
||||
export APP_TEAM_ID
|
||||
return
|
||||
fi
|
||||
if [[ "${identity}" =~ \(([A-Z0-9]{10})\)$ ]]; then
|
||||
APP_TEAM_ID="${BASH_REMATCH[1]}"
|
||||
export APP_TEAM_ID
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_signing_mode() {
|
||||
if [[ -n "${SIGNING_MODE}" ]]; then
|
||||
export_team_id_from_identity "${APP_IDENTITY:-}"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${APP_IDENTITY:-}" ]]; then
|
||||
if has_signing_identity "${APP_IDENTITY}"; then
|
||||
export_team_id_from_identity "${APP_IDENTITY}"
|
||||
SIGNING_MODE="identity"
|
||||
return
|
||||
fi
|
||||
log "WARN: APP_IDENTITY not found in Keychain; falling back to adhoc signing."
|
||||
SIGNING_MODE="adhoc"
|
||||
return
|
||||
fi
|
||||
|
||||
local candidate=""
|
||||
for candidate in \
|
||||
"Developer ID Application: Peter Steinberger (Y5PE65HELJ)" \
|
||||
"CodexBar Development"
|
||||
do
|
||||
if has_signing_identity "${candidate}"; then
|
||||
APP_IDENTITY="${candidate}"
|
||||
export APP_IDENTITY
|
||||
export_team_id_from_identity "${APP_IDENTITY}"
|
||||
SIGNING_MODE="identity"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
candidate="$(detect_codesigning_identity)"
|
||||
if [[ -n "${candidate}" ]]; then
|
||||
APP_IDENTITY="${candidate}"
|
||||
export APP_IDENTITY
|
||||
export_team_id_from_identity "${APP_IDENTITY}"
|
||||
SIGNING_MODE="identity"
|
||||
return
|
||||
fi
|
||||
|
||||
SIGNING_MODE="adhoc"
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local label="$1"; shift
|
||||
log "==> ${label}"
|
||||
if ! "$@"; then
|
||||
fail "${label} failed"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -d "${LOCK_DIR}" ]]; then
|
||||
rm -rf "${LOCK_DIR}"
|
||||
fi
|
||||
}
|
||||
|
||||
acquire_lock() {
|
||||
while true; do
|
||||
if mkdir "${LOCK_DIR}" 2>/dev/null; then
|
||||
echo "$$" > "${LOCK_PID_FILE}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local existing_pid=""
|
||||
if [[ -f "${LOCK_PID_FILE}" ]]; then
|
||||
existing_pid="$(cat "${LOCK_PID_FILE}" 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [[ -n "${existing_pid}" ]] && kill -0 "${existing_pid}" 2>/dev/null; then
|
||||
if [[ "${WAIT_FOR_LOCK}" == "1" ]]; then
|
||||
log "==> Another agent is compiling (pid ${existing_pid}); waiting..."
|
||||
while kill -0 "${existing_pid}" 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
continue
|
||||
fi
|
||||
log "==> Another agent is compiling (pid ${existing_pid}); re-run with --wait."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rm -rf "${LOCK_DIR}"
|
||||
done
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
kill_claude_probes() {
|
||||
# CodexBar spawns `claude /usage` + `/status` in a PTY; if we kill the app mid-probe we can orphan them.
|
||||
pkill -f "claude (/status|/usage) --allowed-tools" 2>/dev/null || true
|
||||
sleep 0.2
|
||||
pkill -9 -f "claude (/status|/usage) --allowed-tools" 2>/dev/null || true
|
||||
}
|
||||
|
||||
kill_all_codexbar() {
|
||||
is_running() {
|
||||
pgrep -f "${APP_PROCESS_PATTERN}" >/dev/null 2>&1 \
|
||||
|| pgrep -f "${DEBUG_PROCESS_PATTERN}" >/dev/null 2>&1 \
|
||||
|| pgrep -f "${RELEASE_PROCESS_PATTERN}" >/dev/null 2>&1 \
|
||||
|| pgrep -x "CodexBar" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Phase 1: request termination (give the app time to exit cleanly).
|
||||
for _ in {1..25}; do
|
||||
pkill -f "${APP_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -f "${DEBUG_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -f "${RELEASE_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -x "CodexBar" 2>/dev/null || true
|
||||
if ! is_running; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
# Phase 2: force kill any stragglers (avoids `open -n` creating multiple instances).
|
||||
pkill -9 -f "${APP_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -9 -f "${DEBUG_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -9 -f "${RELEASE_PROCESS_PATTERN}" 2>/dev/null || true
|
||||
pkill -9 -x "CodexBar" 2>/dev/null || true
|
||||
|
||||
for _ in {1..25}; do
|
||||
if ! is_running; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
fail "Failed to kill all CodexBar instances."
|
||||
}
|
||||
|
||||
# 1) Ensure a single runner instance.
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
--wait|-w) WAIT_FOR_LOCK=1 ;;
|
||||
--test|-t) RUN_TESTS=1 ;;
|
||||
--debug-lldb) DEBUG_LLDB=1 ;;
|
||||
--clear-adhoc-keychain) CLEAR_ADHOC_KEYCHAIN=1 ;;
|
||||
--release-universal) RELEASE_ARCHES="arm64 x86_64" ;;
|
||||
--release-arches=*) RELEASE_ARCHES="${arg#*=}" ;;
|
||||
--help|-h)
|
||||
log "Usage: $(basename "$0") [--wait] [--test] [--debug-lldb] [--clear-adhoc-keychain] [--release-universal] [--release-arches=\"arm64 x86_64\"]"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ensure_swift_version
|
||||
resolve_signing_mode
|
||||
if [[ "${CLEAR_ADHOC_KEYCHAIN}" == "1" && "${SIGNING_MODE}" != "adhoc" ]]; then
|
||||
fail "--clear-adhoc-keychain is only supported when using adhoc signing."
|
||||
fi
|
||||
if [[ "${SIGNING_MODE}" == "adhoc" ]]; then
|
||||
log "==> Signing: adhoc (set APP_IDENTITY or install a dev cert to avoid keychain prompts)"
|
||||
else
|
||||
log "==> Signing: ${APP_IDENTITY:-Developer ID Application}"
|
||||
fi
|
||||
|
||||
acquire_lock
|
||||
|
||||
# 2) Kill all running CodexBar instances (debug, release, bundled).
|
||||
log "==> Killing existing CodexBar instances"
|
||||
kill_all_codexbar
|
||||
kill_claude_probes
|
||||
|
||||
# 2.5) Optionally delete keychain entries to avoid permission prompts with adhoc signing
|
||||
# (adhoc signature changes on every build, making old keychain entries inaccessible)
|
||||
if [[ "${SIGNING_MODE:-adhoc}" == "adhoc" && "${CLEAR_ADHOC_KEYCHAIN}" == "1" ]]; then
|
||||
log "==> Clearing CodexBar keychain entries (adhoc signing)"
|
||||
# Clear both the legacy keychain store and the current cache service when developers explicitly want a clean reset
|
||||
# of CodexBar-owned keychain state for ad-hoc builds.
|
||||
delete_keychain_service_items "com.steipete.CodexBar"
|
||||
delete_keychain_service_items "com.steipete.codexbar.cache"
|
||||
elif [[ "${SIGNING_MODE:-adhoc}" == "adhoc" ]]; then
|
||||
log "==> Preserving CodexBar keychain entries (pass --clear-adhoc-keychain to reset adhoc keychain state)"
|
||||
fi
|
||||
|
||||
# 3) Package (release build happens inside package_app.sh).
|
||||
if [[ "${RUN_TESTS}" == "1" ]]; then
|
||||
run_step "sharded swift tests" "${ROOT_DIR}/Scripts/test.sh"
|
||||
fi
|
||||
if [[ "${DEBUG_LLDB}" == "1" && -n "${RELEASE_ARCHES}" ]]; then
|
||||
fail "--release-arches is only supported for release packaging"
|
||||
fi
|
||||
HOST_ARCH="$(uname -m)"
|
||||
ARCHES_VALUE="${HOST_ARCH}"
|
||||
if [[ -n "${RELEASE_ARCHES}" ]]; then
|
||||
ARCHES_VALUE="${RELEASE_ARCHES}"
|
||||
fi
|
||||
PACKAGE_ENV=(
|
||||
ARCHES="${ARCHES_VALUE}"
|
||||
)
|
||||
if [[ "${DEBUG_LLDB}" == "1" ]]; then
|
||||
run_step "package app" env CODEXBAR_ALLOW_LLDB=1 "${PACKAGE_ENV[@]}" "${ROOT_DIR}/Scripts/package_app.sh" debug
|
||||
else
|
||||
if [[ -n "${SIGNING_MODE}" ]]; then
|
||||
run_step "package app" env CODEXBAR_SIGNING="${SIGNING_MODE}" "${PACKAGE_ENV[@]}" "${ROOT_DIR}/Scripts/package_app.sh"
|
||||
else
|
||||
run_step "package app" env "${PACKAGE_ENV[@]}" "${ROOT_DIR}/Scripts/package_app.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4) Launch the packaged app.
|
||||
log "==> launch app"
|
||||
if ! open "${APP_BUNDLE}"; then
|
||||
log "WARN: launch app returned non-zero; falling back to direct binary launch."
|
||||
"${APP_BUNDLE}/Contents/MacOS/CodexBar" >/dev/null 2>&1 &
|
||||
disown
|
||||
fi
|
||||
|
||||
# 5) Verify the app stays up for at least a moment (launch can be >1s on some systems).
|
||||
for _ in {1..10}; do
|
||||
if pgrep -f "${APP_PROCESS_PATTERN}" >/dev/null 2>&1; then
|
||||
log "OK: CodexBar is running."
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.4
|
||||
done
|
||||
fail "App exited immediately. Check crash logs in Console.app (User Reports)."
|
||||
Reference in New Issue
Block a user