Working Auto Updater

pantha007 4 months ago

This script is HIGHLY EXPERIMENTAL.
I have only done very limited testing.
It may contain bugs that I ( and ChatGPT ) may have not caught.
Running it without understanding what it does could completely break your Traccar installation, delete your config.

Hi all I have been working on an auto update script for Traccar running on my Raspberry Pi 5. I have turned it into a step by step tutorial if anyone wants it :-). It will backup your existing config / check your current version / download the latest release / install the new release ( if found) / restore backup and restart the server :-)

  1. Install jq (required to parse the GitHub API responses)
sudo apt-get install jq
  1. Create the main update file
nano ~/traccar-auto-update.sh

Paste in the following :

#!/usr/bin/env bash

set -Eeuo pipefail

DRY_RUN=0
if [[ "${1:-}" == "--dry-run" ]]; then
    DRY_RUN=1
fi

# --- re-exec as root if needed ---
if [[ $EUID -ne 0 && $DRY_RUN -eq 0 ]]; then
  exec sudo -E bash "$0" "$@"
fi

# --- check dependencies ---
for cmd in jq curl unzip systemctl tar; do
    command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is required. Please install it first."; exit 1; }
done

# --- paths & settings ---
TRACCAR_DIR="/opt/traccar"
TRACCAR_USER="traccar"
TRACCAR_CONF_FILE="$TRACCAR_DIR/conf/traccar.xml"
TMP_DIR="/tmp/traccar-update"
BACKUP_DIR="/opt/traccar-backup"
STATE_FILE="/home/craig/traccar-current-version.txt"
LOG_TAG="Traccar-Update"

log(){ echo "$(date '+%F %T') $LOG_TAG: $*"; }

trim_ver(){ printf "%s" "$1" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^v//' ; }

get_installed_version(){
  local v=""
  if [[ -f "$TRACCAR_CONF_FILE" ]]; then
    v=$(grep -oP '<entry key="version.build">\K(.*?)(?=</entry>)' "$TRACCAR_CONF_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$STATE_FILE" ]]; then
    v=$(head -n1 "$STATE_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$TRACCAR_DIR/tracker-server.jar" ]]; then
    v=$(unzip -p "$TRACCAR_DIR/tracker-server.jar" META-INF/MANIFEST.MF 2>/dev/null \
         | awk -F': ' '/^Implementation-Version:/ {print $2; exit}' || true)
  fi
  trim_ver "${v:-}"
}

get_latest_version(){
  local tag=""
  tag=$(curl -fsSL https://api.github.com/repos/traccar/traccar/releases/latest | jq -r '.tag_name' || true)
  trim_ver "${tag:-}"
}

# --- start ---
mkdir -p "$TMP_DIR" "$BACKUP_DIR"

INSTALLED_VERSION="$(get_installed_version)"
LATEST_VERSION="$(get_latest_version)"

if [[ -z "$LATEST_VERSION" ]]; then
  log "ERROR: Could not determine latest version from GitHub. Aborting."
  exit 1
fi

log "Installed version: ${INSTALLED_VERSION:-unknown}"
log "Latest version: $LATEST_VERSION"

if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
  log "Installed version ($INSTALLED_VERSION) matches latest ($LATEST_VERSION). No update required."
  exit 0
fi

log "Update available: $LATEST_VERSION"

if [[ $DRY_RUN -eq 1 ]]; then
    log "DRY-RUN: Would back up config and installation to $BACKUP_DIR"
    log "DRY-RUN: Would download installer for version $LATEST_VERSION"
    log "DRY-RUN: Would stop traccar, install new version, restore config, adjust permissions, and start service"
    exit 0
fi

# --- backup config and installation ---
TS="$(date +%F_%H-%M-%S)"
if [[ -f "$TRACCAR_CONF_FILE" ]]; then
  CONF_BAK="$BACKUP_DIR/traccar.xml.$TS"
  cp -a "$TRACCAR_CONF_FILE" "$CONF_BAK"
  log "Backed up config to $CONF_BAK"
else
  log "WARNING: $TRACCAR_CONF_FILE not found; continuing."
fi

if [[ -d "$TRACCAR_DIR" ]]; then
  TAR_BAK="$BACKUP_DIR/traccar-${INSTALLED_VERSION:-unknown}-$TS.tgz"
  tar -C /opt -czf "$TAR_BAK" traccar || { log "WARNING: backup archive failed"; }
  log "Backed up install to $TAR_BAK"
fi

# --- determine architecture ---
ARCH=$(uname -m)
case "$ARCH" in
  x86_64) ZIP_ARCH="x64" ;;
  aarch64|arm64) ZIP_ARCH="arm64" ;;
  armv7*) ZIP_ARCH="arm" ;;
  *) log "Unsupported architecture: $ARCH"; exit 1 ;;
esac

# --- fetch release zip ---
VNUM="$LATEST_VERSION"
ZIP_URL="https://github.com/traccar/traccar/releases/download/v$VNUM/traccar-linux-$ZIP_ARCH-$VNUM.zip"
ZIP_FILE="$TMP_DIR/traccar-$VNUM.zip"

log "Downloading $ZIP_URL ..."
curl -fL "$ZIP_URL" -o "$ZIP_FILE"
log "Download saved to $ZIP_FILE"

# --- extract traccar.run and install ---
rm -rf "$TMP_DIR/new"
mkdir -p "$TMP_DIR/new"
unzip -oq "$ZIP_FILE" -d "$TMP_DIR/new"

if [[ ! -f "$TMP_DIR/new/traccar.run" ]]; then
  log "ERROR: traccar.run not found in ZIP. Aborting."
  exit 1
fi

chmod +x "$TMP_DIR/new/traccar.run"

log "Stopping Traccar..."
systemctl stop traccar || log "Warning: failed to stop traccar service"

log "Installing to $TRACCAR_DIR ..."
"$TMP_DIR/new/traccar.run" --accept --target "$TRACCAR_DIR"

if [[ -f "${CONF_BAK:-/nonexistent}" ]]; then
  cp -a "$CONF_BAK" "$TRACCAR_CONF_FILE"
  log "Restored config from $CONF_BAK"
fi

# --- permissions ---
chown -R "$TRACCAR_USER":"$TRACCAR_USER" "$TRACCAR_DIR"
chmod -R 755 "$TRACCAR_DIR"
[[ -f "$TRACCAR_CONF_FILE" ]] && chmod 644 "$TRACCAR_CONF_FILE"

log "Starting Traccar..."
systemctl daemon-reload
if ! systemctl start traccar; then
  log "ERROR: Failed to start traccar service"
  exit 1
fi

# --- cleanup ---
rm -rf "$TMP_DIR/new" "$ZIP_FILE"

# --- write state file ---
printf "%s\n" "$VNUM" > "$STATE_FILE"

log "Update completed to $VNUM."

THEN SAVE AND EXIT

  1. Make the file executable
chmod +x ~/traccar-auto-update.sh
  1. Then you can run it with:
sudo ~/traccar-auto-update.sh
  1. Create a systemd service file NOTE: ExecStart= Change Path As Needed
sudo nano /etc/systemd/system/traccar-auto-update.service

Paste in the following :

[Unit]
Description=Traccar Auto Update Service
After=network.target

[Service]
Type=oneshot
ExecStart=/home/craig/traccar-auto-update.sh
StandardOutput=append:/var/log/traccar-auto-update.log
StandardError=append:/var/log/traccar-auto-update.log

[Install]
WantedBy=multi-user.target

THEN SAVE AND EXIT

  1. Create a systemd timer Currently set to check for updates at 03:00
sudo nano /etc/systemd/system/traccar-auto-update.timer

Paste in the following :

[Unit]
Description=Daily Traccar Auto Update Timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
Unit=traccar-auto-update.service

[Install]
WantedBy=timers.target

THEN SAVE AND EXIT

  1. Enable and start the timer

Reload systemd so it sees any edits

sudo systemctl daemon-reload  

Enable + start the timer

sudo systemctl enable --now traccar-auto-update.timer  

Check if it’s running

systemctl list-timers --all | grep traccar-auto-update
  1. Optional: Test manually
sudo systemctl start traccar-auto-update.service
tail -n 50 /home/craig/traccar-auto-update.log

Change /home/craig to your own location

Anton Tananaev 4 months ago

Are you sure you wrote this script? Or ChatGPT did? And have you tested it?

pantha007 4 months ago

I could never have created the main script without GPT's help ( it took a week of arguing with it to get it to this stage ) and yes it's running on my Pi :-)

Anton Tananaev 4 months ago

Well, unfortunately it's incorrect. The version check won't work. It tries to read a version from the config file, which doesn't exist, unless you manually define it.

pantha007 4 months ago

That was the old script damm , thanks for pointing out this issue please see below the fixed one which also has a Dry Run mode added ( if this works ok i need to find out how to edit my original post lol ) please dont be too rough on me this is the first thing I have put together (with a lot of help from chat GPT)

#!/usr/bin/env bash

set -Eeuo pipefail

DRY_RUN=0
if [[ "${1:-}" == "--dry-run" ]]; then
    DRY_RUN=1
fi

# --- re-exec as root if needed ---
if [[ $EUID -ne 0 && $DRY_RUN -eq 0 ]]; then
  exec sudo -E bash "$0" "$@"
fi

# --- check dependencies ---
for cmd in jq curl unzip systemctl tar; do
    command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is required. Please install it first."; exit 1; }
done

# --- paths & settings ---
TRACCAR_DIR="/opt/traccar"
TRACCAR_USER="traccar"
TRACCAR_CONF_FILE="$TRACCAR_DIR/conf/traccar.xml"
TMP_DIR="/tmp/traccar-update"
BACKUP_DIR="/opt/traccar-backup"
STATE_FILE="/home/craig/traccar-current-version.txt"
LOG_TAG="Traccar-Update"

log(){ echo "$(date '+%F %T') $LOG_TAG: $*"; }

trim_ver(){ printf "%s" "$1" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^v//' ; }

get_installed_version(){
  local v=""
  if [[ -f "$TRACCAR_CONF_FILE" ]]; then
    v=$(grep -oP '<entry key="version.build">\K(.*?)(?=</entry>)' "$TRACCAR_CONF_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$STATE_FILE" ]]; then
    v=$(head -n1 "$STATE_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$TRACCAR_DIR/tracker-server.jar" ]]; then
    v=$(unzip -p "$TRACCAR_DIR/tracker-server.jar" META-INF/MANIFEST.MF 2>/dev/null \
         | awk -F': ' '/^Implementation-Version:/ {print $2; exit}' || true)
  fi
  trim_ver "${v:-}"
}

get_latest_version(){
  local tag=""
  tag=$(curl -fsSL https://api.github.com/repos/traccar/traccar/releases/latest | jq -r '.tag_name' || true)
  trim_ver "${tag:-}"
}

# --- start ---
mkdir -p "$TMP_DIR" "$BACKUP_DIR"

INSTALLED_VERSION="$(get_installed_version)"
LATEST_VERSION="$(get_latest_version)"

if [[ -z "$LATEST_VERSION" ]]; then
  log "ERROR: Could not determine latest version from GitHub. Aborting."
  exit 1
fi

log "Installed version: ${INSTALLED_VERSION:-unknown}"
log "Latest version: $LATEST_VERSION"

if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
  log "Installed version ($INSTALLED_VERSION) matches latest ($LATEST_VERSION). No update required."
  exit 0
fi

log "Update available: $LATEST_VERSION"

if [[ $DRY_RUN -eq 1 ]]; then
    log "DRY-RUN: Would back up config and installation to $BACKUP_DIR"
    log "DRY-RUN: Would download installer for version $LATEST_VERSION"
    log "DRY-RUN: Would stop traccar, install new version, restore config, adjust permissions, and start service"
    exit 0
fi

# --- backup config and installation ---
TS="$(date +%F_%H-%M-%S)"
if [[ -f "$TRACCAR_CONF_FILE" ]]; then
  CONF_BAK="$BACKUP_DIR/traccar.xml.$TS"
  cp -a "$TRACCAR_CONF_FILE" "$CONF_BAK"
  log "Backed up config to $CONF_BAK"
else
  log "WARNING: $TRACCAR_CONF_FILE not found; continuing."
fi

if [[ -d "$TRACCAR_DIR" ]]; then
  TAR_BAK="$BACKUP_DIR/traccar-${INSTALLED_VERSION:-unknown}-$TS.tgz"
  tar -C /opt -czf "$TAR_BAK" traccar || { log "WARNING: backup archive failed"; }
  log "Backed up install to $TAR_BAK"
fi

# --- determine architecture ---
ARCH=$(uname -m)
case "$ARCH" in
  x86_64) ZIP_ARCH="x64" ;;
  aarch64|arm64) ZIP_ARCH="arm64" ;;
  armv7*) ZIP_ARCH="arm" ;;
  *) log "Unsupported architecture: $ARCH"; exit 1 ;;
esac

# --- fetch release zip ---
VNUM="$LATEST_VERSION"
ZIP_URL="https://github.com/traccar/traccar/releases/download/v$VNUM/traccar-linux-$ZIP_ARCH-$VNUM.zip"
ZIP_FILE="$TMP_DIR/traccar-$VNUM.zip"

log "Downloading $ZIP_URL ..."
curl -fL "$ZIP_URL" -o "$ZIP_FILE"
log "Download saved to $ZIP_FILE"

# --- extract traccar.run and install ---
rm -rf "$TMP_DIR/new"
mkdir -p "$TMP_DIR/new"
unzip -oq "$ZIP_FILE" -d "$TMP_DIR/new"

if [[ ! -f "$TMP_DIR/new/traccar.run" ]]; then
  log "ERROR: traccar.run not found in ZIP. Aborting."
  exit 1
fi

chmod +x "$TMP_DIR/new/traccar.run"

log "Stopping Traccar..."
systemctl stop traccar || log "Warning: failed to stop traccar service"

log "Installing to $TRACCAR_DIR ..."
"$TMP_DIR/new/traccar.run" --accept --target "$TRACCAR_DIR"

if [[ -f "${CONF_BAK:-/nonexistent}" ]]; then
  cp -a "$CONF_BAK" "$TRACCAR_CONF_FILE"
  log "Restored config from $CONF_BAK"
fi

# --- permissions ---
chown -R "$TRACCAR_USER":"$TRACCAR_USER" "$TRACCAR_DIR"
chmod -R 755 "$TRACCAR_DIR"
[[ -f "$TRACCAR_CONF_FILE" ]] && chmod 644 "$TRACCAR_CONF_FILE"

log "Starting Traccar..."
systemctl daemon-reload
if ! systemctl start traccar; then
  log "ERROR: Failed to start traccar service"
  exit 1
fi

# --- cleanup ---
rm -rf "$TMP_DIR/new" "$ZIP_FILE"

# --- write state file ---
printf "%s\n" "$VNUM" > "$STATE_FILE"

log "Update completed to $VNUM."
Anton Tananaev 4 months ago

Can you share ChatGPT chat for this generation?

pantha007 4 months ago

this was the first main chat "https://chatgpt.com/share/68adc955-9908-8006-9cb3-7bc55c853633" and this was the second part where i basically asked GPT to check its own work and fix it "https://chatgpt.com/share/68adc9e6-f580-800d-88c2-8fae8151ae91" I gotta warn you the first one was a very long conversation lol. I just hope this works ok for everyone at some point :-)

Anton Tananaev 4 months ago

It looks like the script was fixed just now. Or am I misreading this? So the original scrip wasn't "old".

pantha007 4 months ago

you are correct, the original script did run on mine ( dont know why ) so i asked GPT for help today

Anton Tananaev 4 months ago

I appreciate sharing it, but I think you should provide very clear warnings about the code. It seems like you did very limited testing. It seems like you might have fixed the issue I pointed out, but I'm worried there are more errors. And with something as important as updating, someone can completely mess up their setup very quickly.

Anton Tananaev 4 months ago

If you really want to automate updates, I would probably recommend using Docker.

pantha007 4 months ago

I shall delete this post and make a new one with everything up to date and add warnings and note this is still being tested. Thankyou for looking at it :-) and pointing out the issues :-).

Craig

Anton Tananaev 4 months ago

It's fine to keep this. I can update the original post for you if needed.

pantha007 4 months ago

amazing thankyou if you could add the fixed code and also add some warnings please, something like

This script is HIGHLY EXPERIMENTAL.
I have only done very limited testing.
It may contain bugs that I ( and ChatGPT ) may have not caught.
Running it without understanding what it does could completely break your Traccar installation, delete your config.

If you decide to try this:

Use --dry-run first. That will show what it would do without making changes.

Many thanks again for your help :-)

Anton Tananaev 4 months ago

Updated the script and description.