Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion templates/docker-compose/MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ make restore
```bash
make backup
```
2. Run the `upgrade` command:
2. Consider upgrading dependencies (MySQL, Redis, Meilisearch, etc) to new supported versions, and then run the `upgrade` command:
```bash
make upgrade
```
Expand Down
240 changes: 234 additions & 6 deletions templates/docker-monolithic/upgrade.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
#!/bin/bash
set -eu -o pipefail

# Generate unique log filename
generate_log_filename() {
local base_name="upgrade"
local ext="log"
local filename="${base_name}.${ext}"
local counter=1

while [ -f "$filename" ]; do
filename="${base_name}-${counter}.${ext}"
((counter++))
done

echo "$filename"
}

# Initialize log file
LOG_FILE="$(generate_log_filename)"
touch "$LOG_FILE"

# Log function - writes to log file
log_debug() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}

# Error exit function - notifies user about log file
error_exit() {
echo "ERROR: $1" >&2
echo "A debug log is available in: $LOG_FILE"
exit 1
Comment on lines +29 to +32
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message states "A debug log is available in:" but the log file is not touched or created if an error occurs before line 21. If error_exit is called before LOG_FILE is initialized (e.g., during argument parsing), this will reference an undefined variable or non-existent file. Consider initializing LOG_FILE earlier or checking if it exists before printing this message.

Copilot uses AI. Check for mistakes.
}

usage="Options:
-h,--help Display this help and exit.
-r,--ref=5.x Git ref (commit sha, ref name, tag) to run the script on.
Expand Down Expand Up @@ -37,6 +68,8 @@ while [[ "$#" -gt 0 ]]; do
shift
done

COMPOSE_FILE_DOWNLOAD_URL="https://raw.githubusercontent.com/supportpal/helpdesk-install/${ref}/templates/docker-monolithic/docker-compose.yml"

# usage: version_ge <installed_version> <minimum_version>
version_ge() {
if ! [ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]; then
Expand Down Expand Up @@ -81,7 +114,7 @@ backup() {
}

update_compose_files() {
curl -fLsS https://raw.githubusercontent.com/supportpal/helpdesk-install/"${ref}"/templates/docker-monolithic/docker-compose.yml -o docker-compose.yml
curl -fLsS "${COMPOSE_FILE_DOWNLOAD_URL}" -o docker-compose.yml
}

update_volumes() {
Expand All @@ -107,21 +140,216 @@ update_env() {
fi
}

drop_meilisearch_data() {
echo "Preparing Meilisearch for upgrade..."

# Stop Meilisearch service
echo "Stopping Meilisearch service..."
if ! docker compose exec supportpal sudo sv stop 00meilisearch; then
error_exit "Failed to stop Meilisearch service"
fi
echo "✓ Meilisearch service stopped"

# Clear Meilisearch database directory (including hidden files)
echo "Clearing Meilisearch database directory..."
if ! docker compose exec supportpal bash -c 'sudo rm -rf "${MEILI_DB_PATH:?}"/* "${MEILI_DB_PATH:?}"/.[!.]*'; then
error_exit "Failed to clear Meilisearch database directory"
fi
echo "✓ Meilisearch database directory cleared"
}

# Helper function to parse version from meilisearch --version output
parse_meilisearch_version() {
local version_output="$1"
local pkg_version

log_debug "Raw version output: $version_output"

# Extract version number from output like "meilisearch 1.10.3"
if ! pkg_version="$(echo "$version_output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')"; then
log_debug "Expected format: 'meilisearch x.y.z'"
log_debug "Actual output: $version_output"
error_exit "Failed to parse version from meilisearch --version output"
fi

log_debug "Extracted Meilisearch version: $pkg_version"
echo "$pkg_version"
}

# Usage: get_current_meilisearch_version <container_id>
get_current_meilisearch_version() {
local cid="$1"
local version

log_debug "Getting Meilisearch version from container: $cid"

# Get version directly using meilisearch --version command
if ! version="$(docker exec "$cid" meilisearch --version 2>&1)"; then
log_debug "Make sure the container is running and has meilisearch binary"
log_debug "Container logs:"
docker logs --tail 10 "$cid" >> "$LOG_FILE" 2>&1 || true
error_exit "Failed to get Meilisearch version from container $cid"
fi

parse_meilisearch_version "$version"
}

get_next_meilisearch_version() {
log_debug "Fetching next Meilisearch version from docker-compose file..."
log_debug "Download URL: $COMPOSE_FILE_DOWNLOAD_URL"

local image
if ! image=$(curl -fsSL "${COMPOSE_FILE_DOWNLOAD_URL}" 2>&1 | grep -m1 -E '^[[:space:]]*image:' | sed -E "s/^[[:space:]]*image:[[:space:]]*//; s/^['\"]//; s/['\"]$//"); then
log_debug "URL: $COMPOSE_FILE_DOWNLOAD_URL"
log_debug "Check if the URL is accessible and contains valid YAML"
error_exit "Failed to fetch or parse docker-compose file"
fi

if [ -z "$image" ]; then
log_debug "Expected to find a line like 'image: supportpal/helpdesk:latest'"
error_exit "Could not extract image name from docker-compose file"
fi

log_debug "Found image: $image"

# Get version directly using meilisearch --version without starting the full container
log_debug "Getting Meilisearch version from image..."

local version
if ! version="$(docker run --rm --entrypoint meilisearch "$image" --version 2>&1)"; then
log_debug "Check if the image exists and contains meilisearch binary"
log_debug "Docker run output: $version"
error_exit "Failed to get Meilisearch version from image: $image"
fi

parse_meilisearch_version "$version"
}

# Check if a version requires data dumping for upgrade
meili_requires_upgrade() {
local MEILISEARCH_DUMPLESS_VERSION="1.12.0"
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name MEILISEARCH_DUMPLESS_VERSION uses all uppercase, which is appropriate for a constant. However, it should be declared with readonly to make it a true constant and prevent accidental modification.

Suggested change
local MEILISEARCH_DUMPLESS_VERSION="1.12.0"
local -r MEILISEARCH_DUMPLESS_VERSION="1.12.0"

Copilot uses AI. Check for mistakes.
local from_version="$1"
local to_version="$2"

log_debug "Checking if Meilisearch requires upgrading"
log_debug "Current version: $from_version"
log_debug "Target version: $to_version"
log_debug "Dumpless version threshold: $MEILISEARCH_DUMPLESS_VERSION"

# Validate input versions
if [ -z "$from_version" ] || [ -z "$to_version" ]; then
log_debug "from_version='$from_version', to_version='$to_version'"
error_exit "Both from_version and to_version must be provided"
fi

# Check if versions are equal - no dump needed if not upgrading
if [ "$from_version" = "$to_version" ]; then
log_debug "No dump required - versions are identical (no upgrade needed)"
return 1 # false - no dump required
fi

# Check version format (basic validation)
if ! echo "$from_version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then
log_debug "WARNING: from_version '$from_version' doesn't match expected format (x.y.z)"
fi

if ! echo "$to_version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then
log_debug "WARNING: to_version '$to_version' doesn't match expected format (x.y.z)"
fi

# If upgrading from < 1.12.0 to any version, dump is required
local comparison_result
comparison_result="$(printf '%s\n' "$from_version" "$MEILISEARCH_DUMPLESS_VERSION" | sort -V | head -n1)"

if [ "$comparison_result" = "$from_version" ] && [ "$from_version" != "$MEILISEARCH_DUMPLESS_VERSION" ]; then
log_debug "Upgrading from pre-$MEILISEARCH_DUMPLESS_VERSION version"
return 0 # true - dump required
else
log_debug "No dump required - version $from_version >= $MEILISEARCH_DUMPLESS_VERSION"
return 1 # false - no dump required
fi
}

upgrade() {
backup
docker compose down -v
echo "Starting upgrade process..."

# Get current Meilisearch version with error handling
echo "Checking current Meilisearch version..."
local current_container_id
if ! current_container_id=$(docker compose ps -q supportpal 2>/dev/null); then
log_debug "Ensure docker compose is running and supportpal service exists"
error_exit "Failed to get supportpal container ID"
fi

if [ -z "$current_container_id" ]; then
log_debug "Start the containers with: docker compose up -d"
error_exit "supportpal container is not running"
fi

log_debug "Found supportpal container: $current_container_id"

local current_meili_version
if ! current_meili_version="$(get_current_meilisearch_version "$current_container_id")"; then
log_debug "Check if Meilisearch is properly configured in the supportpal container"
error_exit "Failed to get current Meilisearch version"
fi

echo "✓ Current Meilisearch version: $current_meili_version"

# Get next Meilisearch version with error handling
echo "Checking target Meilisearch version..."
local next_meili_version
if ! next_meili_version="$(get_next_meilisearch_version)"; then
log_debug "Check network connectivity and docker image availability"
error_exit "Failed to get target Meilisearch version"
fi

echo "✓ Target Meilisearch version: $next_meili_version"

# Check if dump is required
if meili_requires_upgrade "$current_meili_version" "$next_meili_version"; then
echo "✓ Meilisearch upgrade is required."
echo "! The Meilisearch database will be dropped and re-indexed... cancel (CTRL+C) if you're not happy to proceed..."
sleep 10
drop_meilisearch_data
fi

echo "Stopping containers..."
if ! docker compose down -v; then
log_debug "Check docker compose status and try manually: docker compose down -v"
error_exit "Failed to stop containers"
fi

echo "Updating volumes..."
update_volumes

echo "Updating environment configuration..."
update_env

echo "Updating docker-compose files..."
update_compose_files

if [ "${only_files}" = true ]; then
echo "✓ Files updated successfully (--only-files specified)"
exit 0
fi

COMPOSE_PARALLEL_LIMIT=1 docker compose up -d
docker compose exec supportpal bash -c "bash /init/upgrade-helpdesk.sh"
echo "Starting upgraded containers..."
if ! COMPOSE_PARALLEL_LIMIT=1 docker compose up -d; then
log_debug "Check docker compose logs: docker compose logs"
error_exit "Failed to start upgraded containers"
fi

echo "Running helpdesk upgrade script..."
if ! docker compose exec supportpal bash -c "bash /init/upgrade-helpdesk.sh"; then
log_debug "Check container logs: docker compose logs supportpal"
error_exit "Helpdesk upgrade script failed"
fi

echo
echo "Upgrade complete!"
echo "Upgrade complete!"
}

check_docker_compose
backup
upgrade
Loading