#!/usr/bin/env bash
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration Variables
# ---------------------------------------------------------------------------
# The installed Command-Runner is expected at INSTALL_DIR/command-runner.
INSTALL_DIR="/p4/common/site/bin" # Directory containing the installed binary.
BINARY_NAME="command-runner"
INSTALLED_BIN="$INSTALL_DIR/$BINARY_NAME"
INSTALL_SCRIPT="install_command-runner.sh"
# Remote URLs for release artifacts. Update these to your trusted HTTPS server.
PROD_REMOTE_BASE_URL="https://swarm.workshop.perforce.com"
DEV_REMOTE_BASE_URL="http://example.com"
if [ "${1:-}" = "-t" ]; then
echo "Using DEV (test) URLs..."
REMOTE_VERSION_URL="${DEV_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/latest_version.txt"
REMOTE_TARBALL_URL="${DEV_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/command-runner-linux-amd64.tar.gz"
REMOTE_CHECKSUM_URL="${DEV_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/command-runner-linux-amd64.tar.gz.sha256"
else
echo "Using PRODUCTION URLs..."
REMOTE_VERSION_URL="${PROD_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/latest_version.txt"
REMOTE_TARBALL_URL="${PROD_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/command-runner-linux-amd64.tar.gz"
REMOTE_CHECKSUM_URL="${PROD_REMOTE_BASE_URL}/downloads/guest/perforce_software/command-runner/packages/command-runner-linux-amd64.tar.gz.sha256"
fi
# Log file for the installer spawned from the update.
LOG_FILE="/tmp/cmd-runner_installer.log"
# Temporary directory for downloads and extraction.
TMP_DIR="/tmp/command-runner-update.$$"
# ---------------------------------------------------------------------------
# Helper Functions
# ---------------------------------------------------------------------------
function msg() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
}
function bail() {
msg "Error: $1"
exit "${2:-1}"
}
# Get the installed version from the existing binary.
function get_installed_version() {
# Try finding the command-runner binary using which, command, and direct paths.
local installed_bin_path=""
# First, check if the command-runner is in the PATH using 'which'
installed_bin_path=$(which command-runner 2>/dev/null || true)
# If not found, check the default install path for command-runner
if [ -z "$installed_bin_path" ] && [ -x "$INSTALLED_BIN" ]; then
installed_bin_path="$INSTALLED_BIN"
fi
# If command-runner binary was found, attempt to extract the version
if [ -n "$installed_bin_path" ]; then
local raw_version
raw_version="$("$installed_bin_path" --version 2>&1 | head -n1 || true)"
# Parse the version if the raw output is valid
local parsed_version
parsed_version=$(echo "$raw_version" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+')
if [ -n "$parsed_version" ]; then
echo "$parsed_version"
else
echo "Error: Version could not be parsed from the installed binary output."
fi
else
echo "Error: command-runner binary not found."
fi
}
# Compare two versions using sort -V.
function is_remote_newer() {
local local_ver="$1"
local remote_ver="$2"
[[ "$local_ver" == "$remote_ver" ]] && return 1
local sorted
sorted=$(printf "%s\n%s\n" "$local_ver" "$remote_ver" | sort -V)
local newest
newest=$(echo "$sorted" | tail -n1)
[[ "$newest" == "$remote_ver" ]] && return 0 || return 1
}
# Get the owner and group from the systemd service file for command-runner.
function get_service_owner() {
local service_file="/etc/systemd/system/${BINARY_NAME}.service"
local user group
user=$(grep '^User=' "$service_file" | cut -d'=' -f2 | tr -d '[:space:]')
group=$(grep '^Group=' "$service_file" | cut -d'=' -f2 | tr -d '[:space:]')
echo "${user:-perforce}:${group:-perforce}"
}
# ---------------------------------------------------------------------------
# Core Update Logic
# ---------------------------------------------------------------------------
# Get the installed version
CURRENT_VERSION="$(get_installed_version)"
[ -z "$CURRENT_VERSION" ] && msg "No installed version found. Proceeding with fresh install..." || msg "Currently installed version: $CURRENT_VERSION"
# Fetch the remote version.
msg "Fetching remote version from $REMOTE_VERSION_URL ..."
remote_version=$(curl -sSf "$REMOTE_VERSION_URL" || bail "Failed to fetch remote version information.")
msg "Remote version: $remote_version"
# Check if the remote version is newer.
if [[ -n "$CURRENT_VERSION" ]] && ! is_remote_newer "$CURRENT_VERSION" "$remote_version"; then
msg "Already up-to-date. No update needed."
exit 0
fi
msg "Proceeding with update to version $remote_version."
# Create temporary working directory to handle tarball and checksum.
TMP_DIR="/tmp/command-runner-update.$$"
mkdir -p "$TMP_DIR" || bail "Failed to create temporary directory."
# Set file paths for tarball and checksum inside the temporary directory.
TARBALL_FILE="$TMP_DIR/command-runner-linux-amd64.tar.gz"
CHECKSUM_FILE="$TMP_DIR/command-runner-linux-amd64.tar.gz.sha256"
# Download the tarball and checksum file into the temporary directory.
msg "Downloading tarball from $REMOTE_TARBALL_URL ..."
curl -fSsL -o "$TARBALL_FILE" "$REMOTE_TARBALL_URL" || bail "Failed to download tarball."
msg "Downloading checksum file from $REMOTE_CHECKSUM_URL ..."
curl -fSsL -o "$CHECKSUM_FILE" "$REMOTE_CHECKSUM_URL" || bail "Failed to download checksum file."
# Verify the checksum for the downloaded tarball.
msg "Verifying checksum for the downloaded tarball..."
# Normalize the checksum file content to remove any path or directory information
awk '{ sub(/.*[\\\/]/, "", $2); print $1" "$2 }' "$CHECKSUM_FILE" > "$CHECKSUM_FILE.tmp" && mv "$CHECKSUM_FILE.tmp" "$CHECKSUM_FILE"
# Change to the temporary directory
cd "$TMP_DIR" || bail "Cannot change directory to $TMP_DIR"
# Now run the checksum verification
sha256sum -c "$(basename "$CHECKSUM_FILE")" || bail "Checksum verification failed."
msg "Checksum verified successfully."
# Extract the tarball in the temporary directory.
msg "Extracting tarball..."
tar -xzf "$TARBALL_FILE" -C "$TMP_DIR" || bail "Extraction failed."
# Proceed with the installation from the temporary directory.
msg "Proceeding with installation from $TMP_DIR/command-runner-linux-amd64..."
# Spawn the installer from the extracted directory.
msg "Spawning installer for version $remote_version..."
pushd "$TMP_DIR/command-runner" >/dev/null
# Determine if the -n flag is needed based on command-runner location
CMD_RUNNER_PATH=$(which command-runner || true)
NEEDS_N_FLAG=""
[[ -n "$CMD_RUNNER_PATH" && "$CMD_RUNNER_PATH" != "$INSTALL_DIR/command-runner" ]] && NEEDS_N_FLAG="-n"
# Get the service owner
msg "Retrieving service owner and group for ${BINARY_NAME}.service..."
SERVICE_OWNER=$(get_service_owner)
SERVICE_USER=${SERVICE_OWNER%%:*}
# Log the owner and user
msg "Service owner: $SERVICE_OWNER"
msg "Service user: $SERVICE_USER"
# Set ownership for the log file
msg "Setting ownership for log file $LOG_FILE to $SERVICE_OWNER"
touch "$LOG_FILE"
chown "$SERVICE_OWNER" "$LOG_FILE"
# Show the command that will be run
msg "Running installer with the following flags: $@"
msg "Additional flags: -f -y -m $NEEDS_N_FLAG"
# Run the installer with the appropriate flags
msg "Spawning installer in the background..."
nohup ./$INSTALL_SCRIPT "$@" -f -y -m $NEEDS_N_FLAG >> >(sudo -u "$SERVICE_USER" tee -a "$LOG_FILE") 2>&1 &
# Confirm the installer has been spawned
msg "Installer has been spawned in the background. Output will be logged to $LOG_FILE."
# Return to the previous directory
popd >/dev/null
msg "Installer process has been started. Please check $LOG_FILE for detailed progress and potential errors."
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #3 | 31315 | Will Kreitzmann |
v1.2.654. - release |
||
| #2 | 31275 | Will Kreitzmann | rel test | ||
| #1 | 31274 | Will Kreitzmann | Initial |