Last active
November 25, 2025 15:12
-
-
Save yankauskas/93c11f122cec5f9ffb48f4bf440bbd92 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # TeamCity macOS Agent Installer (self-logging) | |
| # - Installs prerequisites (CLT, Homebrew, JDK) | |
| # - Downloads agent ZIP from your server | |
| # - Configures and starts the agent | |
| # - Names agent: m4-mac-mini-<short-id> | |
| # - Installs in the current directory by default | |
| # | |
| # This version self-logs and traces to agent-install.log for easier debugging. | |
| set -Eeuo pipefail | |
| # ---- Self-logging & tracing ---- | |
| LOG_FILE="${LOG_FILE:-$(pwd)/agent-install.log}" | |
| # If stdout is not a tty, ensure we still see output in the log | |
| exec > >(tee -a "$LOG_FILE") 2>&1 | |
| set -x | |
| echo "## $(date) :: Starting TeamCity installer (self-logging)" | |
| echo "## whoami: $(whoami)" | |
| echo "## uname: $(uname -a)" | |
| echo "## shell: ${SHELL:-unknown}" | |
| echo "## PATH: $PATH" | |
| echo "## PWD: $(pwd)" | |
| echo "## LOG_FILE: $LOG_FILE" | |
| # ---- Config (override via env) ---- | |
| SERVER_URL="${SERVER_URL:-https://justplay.teamcity.com}" | |
| AGENT_DIR="${AGENT_DIR:-$(pwd)}" # Install in current directory | |
| # Short random suffix for agent name (only lowercase letters & digits) | |
| SHORT_ID="$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c4 || echo rand)" | |
| AGENT_NAME="${AGENT_NAME:-m4-mac-mini-${SHORT_ID}}" | |
| JAVA_VERSION_MAJOR="${JAVA_VERSION_MAJOR:-17}" # Temurin 17 LTS | |
| AUTO_START="${AUTO_START:-true}" # true -> register launchd | |
| BREW_BIN="/opt/homebrew/bin/brew" | |
| echo "==> TeamCity Agent setup for server: $SERVER_URL" | |
| echo "==> Target agent dir (current directory): $AGENT_DIR" | |
| echo "==> Agent name: $AGENT_NAME" | |
| echo "==> Auto-start with launchd: $AUTO_START" | |
| # ---- Helpers ---- | |
| has_cmd() { command -v "$1" >/dev/null 2>&1; } | |
| require_macos_arm() { | |
| if [[ "$(uname -s)" != "Darwin" ]]; then | |
| echo "This script is for macOS. Exiting." >&2; exit 1 | |
| fi | |
| if [[ "$(uname -m)" != "arm64" ]]; then | |
| echo "This Mac is not Apple Silicon (arm64). Continuing, but you may need Rosetta for some tools." >&2 | |
| fi | |
| } | |
| # ---- 1) Ensure Apple Command Line Tools ---- | |
| ensure_xcode_clt() { | |
| if ! pkgutil --pkg-info=com.apple.pkg.CLTools_Executables >/dev/null 2>&1; then | |
| echo "==> Xcode Command Line Tools not found; triggering install (may open a GUI dialog)…" | |
| xcode-select --install || true | |
| echo "==> Waiting for Command Line Tools to be installed…" | |
| # Poll up to ~10 minutes for CLT to appear | |
| for i in $(seq 1 120); do | |
| if pkgutil --pkg-info=com.apple.pkg.CLTools_Executables >/dev/null 2>&1; then | |
| echo "==> Command Line Tools detected." | |
| break | |
| fi | |
| sleep 5 | |
| done | |
| if ! pkgutil --pkg-info=com.apple.pkg.CLTools_Executables >/dev/null 2>&1; then | |
| echo "ERROR: Command Line Tools were not installed. Please complete the GUI installer, then rerun this script." >&2 | |
| exit 1 | |
| fi | |
| else | |
| echo "==> Xcode Command Line Tools already installed." | |
| fi | |
| } | |
| # ---- 2) Ensure Homebrew ---- | |
| ensure_homebrew() { | |
| if ! has_cmd brew; then | |
| echo "==> Installing Homebrew…" | |
| NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| else | |
| echo "==> Homebrew found." | |
| fi | |
| if [[ -x "$BREW_BIN" ]]; then | |
| eval "$($BREW_BIN shellenv)" | |
| fi | |
| } | |
| # ---- 3) Install Java (Temurin ${JAVA_VERSION_MAJOR}) and Git ---- | |
| install_runtime_tools() { | |
| echo "==> Ensuring Git and Open JDK ${JAVA_VERSION_MAJOR}…" | |
| brew update >/dev/null || true | |
| brew list --formula git >/dev/null 2>&1 || brew install git | |
| if [[ "$JAVA_VERSION_MAJOR" == "17" ]]; then | |
| brew list --cask temurin >/dev/null 2>&1 || brew install --cask temurin | |
| else | |
| brew list --cask "temurin${JAVA_VERSION_MAJOR}" >/dev/null 2>&1 || brew install --cask "temurin${JAVA_VERSION_MAJOR}" | |
| fi | |
| } | |
| # ---- 4) Create <agent>/jre symlink ---- | |
| link_agent_jre() { | |
| mkdir -p "$AGENT_DIR" | |
| local jhome | |
| jhome="$(/usr/libexec/java_home -v "${JAVA_VERSION_MAJOR}" 2>/dev/null || true)" | |
| if [[ -z "${jhome}" ]]; then | |
| echo "ERROR: Could not resolve JAVA_HOME for Java ${JAVA_VERSION_MAJOR}. Is the JDK installed?" >&2 | |
| exit 1 | |
| fi | |
| echo "==> Using JAVA_HOME: $jhome" | |
| if [[ -e "$AGENT_DIR/jre" || -L "$AGENT_DIR/jre" ]]; then | |
| rm -rf "$AGENT_DIR/jre" | |
| fi | |
| ln -s "$jhome" "$AGENT_DIR/jre" | |
| } | |
| # ---- 5) Download and unpack buildAgent.zip ---- | |
| install_agent_zip() { | |
| echo "==> Downloading TeamCity build agent distribution…" | |
| mkdir -p "$AGENT_DIR" | |
| local zip_url="${SERVER_URL%/}/update/buildAgent.zip" | |
| local tmp_zip | |
| tmp_zip="$(mktemp -t tc-agent.XXXXXX.zip)" | |
| curl -fL "$zip_url" -o "$tmp_zip" | |
| echo "==> Unpacking agent to $AGENT_DIR …" | |
| unzip -q -o "$tmp_zip" -d "$AGENT_DIR" | |
| rm -f "$tmp_zip" | |
| } | |
| # ---- 6) Configure buildAgent.properties ---- | |
| configure_agent() { | |
| echo "==> Configuring buildAgent.properties …" | |
| local conf="$AGENT_DIR/conf" | |
| mkdir -p "$conf" | |
| if [[ -f "$conf/buildAgent.dist.properties" && ! -f "$conf/buildAgent.properties" ]]; then | |
| cp "$conf/buildAgent.dist.properties" "$conf/buildAgent.properties" | |
| fi | |
| local props="$conf/buildAgent.properties" | |
| touch "$props" | |
| # Clean existing keys to avoid duplicates | |
| sed -i '' "/^serverUrl=/d" "$props" 2>/dev/null || true | |
| sed -i '' "/^name=/d" "$props" 2>/dev/null || true | |
| sed -i '' "/^workDir=/d" "$props" 2>/dev/null || true | |
| sed -i '' "/^tempDir=/d" "$props" 2>/dev/null || true | |
| { | |
| echo "serverUrl=${SERVER_URL%/}" | |
| echo "name=${AGENT_NAME}" | |
| echo "workDir=${AGENT_DIR}/work" | |
| echo "tempDir=${AGENT_DIR}/temp" | |
| } >> "$props" | |
| mkdir -p "${AGENT_DIR}/work" "${AGENT_DIR}/temp" "${AGENT_DIR}/logs" | |
| } | |
| # ---- 7) Start agent now ---- | |
| start_agent_now() { | |
| echo "==> Starting TeamCity agent…" | |
| bash "$AGENT_DIR/bin/agent.sh" stop || true | |
| bash "$AGENT_DIR/bin/agent.sh" start | |
| echo "==> Tailing log briefly (5s)…" | |
| sleep 5 || true | |
| tail -n 200 "$AGENT_DIR/logs/teamcity-agent.log" 2>/dev/null || true | |
| } | |
| # ---- 8) Register launchd (auto-start on login) ---- | |
| register_launchd() { | |
| if [[ "$AUTO_START" != "true" ]]; then | |
| echo "==> Skipping launchd registration (AUTO_START=$AUTO_START)." | |
| return 0 | |
| fi | |
| echo "==> Registering launchd user agent…" | |
| pushd "$AGENT_DIR" >/dev/null | |
| if [[ -x "./bin/mac.launchd.sh" ]]; then | |
| ./bin/mac.launchd.sh unload || true | |
| ./bin/mac.launchd.sh load | |
| echo "==> LaunchAgent loaded. Agent will start automatically on login." | |
| else | |
| echo "WARNING: mac.launchd.sh not found; skipping launchd setup." | |
| fi | |
| popd >/dev/null | |
| } | |
| # ---- Execute ---- | |
| require_macos_arm | |
| ensure_xcode_clt | |
| ensure_homebrew | |
| install_runtime_tools | |
| link_agent_jre | |
| install_agent_zip | |
| configure_agent | |
| start_agent_now | |
| register_launchd | |
| echo "==> Done. Open $SERVER_URL and authorize this agent if required." | |
| echo "==> Agent name: $AGENT_NAME" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment