Skip to content

Instantly share code, notes, and snippets.

@yankauskas
Last active November 25, 2025 15:12
Show Gist options
  • Select an option

  • Save yankauskas/93c11f122cec5f9ffb48f4bf440bbd92 to your computer and use it in GitHub Desktop.

Select an option

Save yankauskas/93c11f122cec5f9ffb48f4bf440bbd92 to your computer and use it in GitHub Desktop.
#!/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