diff --git a/ct/storyteller.sh b/ct/storyteller.sh index 20c5a254..daafd6b5 100644 --- a/ct/storyteller.sh +++ b/ct/storyteller.sh @@ -1,8 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) - # Copyright (c) 2021-2026 community-scripts ORG -# Author: community-scripts +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://gitlab.com/storyteller-platform/storyteller @@ -30,47 +29,49 @@ function update_script() { exit fi - msg_info "Stopping Service" - systemctl stop storyteller - msg_ok "Stopped Service" + if check_for_gl_release "storyteller" "storyteller-platform/storyteller"; then + msg_info "Stopping Service" + systemctl stop storyteller + msg_ok "Stopped Service" - msg_info "Backing up Data" - cp /opt/storyteller/.env /opt/storyteller_env.bak - msg_ok "Backed up Data" + msg_info "Backing up Data" + cp /opt/storyteller/.env /opt/storyteller_env.bak + msg_ok "Backed up Data" - CLEAN_INSTALL=1 fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" + CLEAN_INSTALL=1 fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" - msg_info "Restoring Configuration" - mv /opt/storyteller_env.bak /opt/storyteller/.env - msg_ok "Restored Configuration" + msg_info "Restoring Configuration" + mv /opt/storyteller_env.bak /opt/storyteller/.env + msg_ok "Restored Configuration" - msg_info "Rebuilding Storyteller" - cd /opt/storyteller - export NODE_OPTIONS="--max-old-space-size=4096" - $STD yarn install --network-timeout 600000 - $STD gcc -g -fPIC -rdynamic -shared web/sqlite/uuid.c -o web/sqlite/uuid.c.so - export CI=1 - export NODE_ENV=production - export NEXT_TELEMETRY_DISABLED=1 - export SQLITE_NATIVE_BINDING=/opt/storyteller/node_modules/better-sqlite3/build/Release/better_sqlite3.node - $STD yarn workspaces foreach -Rpt --from @storyteller-platform/web --exclude @storyteller-platform/eslint run build - mkdir -p /opt/storyteller/web/.next/standalone/web/.next/static - cp -rT /opt/storyteller/web/.next/static /opt/storyteller/web/.next/standalone/web/.next/static - if [[ -d /opt/storyteller/web/public ]]; then - mkdir -p /opt/storyteller/web/.next/standalone/web/public - cp -rT /opt/storyteller/web/public /opt/storyteller/web/.next/standalone/web/public + msg_info "Rebuilding Storyteller" + cd /opt/storyteller + export NODE_OPTIONS="--max-old-space-size=4096" + $STD yarn install --network-timeout 600000 + $STD gcc -g -fPIC -rdynamic -shared web/sqlite/uuid.c -o web/sqlite/uuid.c.so + export CI=1 + export NODE_ENV=production + export NEXT_TELEMETRY_DISABLED=1 + export SQLITE_NATIVE_BINDING=/opt/storyteller/node_modules/better-sqlite3/build/Release/better_sqlite3.node + $STD yarn workspaces foreach -Rpt --from @storyteller-platform/web --exclude @storyteller-platform/eslint run build + mkdir -p /opt/storyteller/web/.next/standalone/web/.next/static + cp -rT /opt/storyteller/web/.next/static /opt/storyteller/web/.next/standalone/web/.next/static + if [[ -d /opt/storyteller/web/public ]]; then + mkdir -p /opt/storyteller/web/.next/standalone/web/public + cp -rT /opt/storyteller/web/public /opt/storyteller/web/.next/standalone/web/public + fi + mkdir -p /opt/storyteller/web/.next/standalone/web/migrations + cp -rT /opt/storyteller/web/migrations /opt/storyteller/web/.next/standalone/web/migrations + mkdir -p /opt/storyteller/web/.next/standalone/web/sqlite + cp -rT /opt/storyteller/web/sqlite /opt/storyteller/web/.next/standalone/web/sqlite + ln -sf /opt/storyteller/.env /opt/storyteller/web/.next/standalone/web/.env + msg_ok "Rebuilt Storyteller" + + msg_info "Starting Service" + systemctl start storyteller + msg_ok "Started Service" + msg_ok "Updated successfully!" fi - mkdir -p /opt/storyteller/web/.next/standalone/web/migrations - cp -rT /opt/storyteller/web/migrations /opt/storyteller/web/.next/standalone/web/migrations - mkdir -p /opt/storyteller/web/.next/standalone/web/sqlite - cp -rT /opt/storyteller/web/sqlite /opt/storyteller/web/.next/standalone/web/sqlite - ln -sf /opt/storyteller/.env /opt/storyteller/web/.next/standalone/web/.env - msg_ok "Rebuilt Storyteller" - - msg_info "Starting Service" - systemctl start storyteller - msg_ok "Started Service" - msg_ok "Updated successfully!" exit } diff --git a/install/storyteller-install.sh b/install/storyteller-install.sh index b5ca86e0..2468c22a 100644 --- a/install/storyteller-install.sh +++ b/install/storyteller-install.sh @@ -20,7 +20,6 @@ $STD apt install -y \ pkg-config \ libsqlite3-dev \ sqlite3 \ - python3 \ python3-setuptools \ ffmpeg msg_ok "Installed Dependencies" @@ -29,12 +28,10 @@ NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "readium" "readium/cli" "prebuild" "latest" "/opt/readium" "readium_linux_x86_64.tar.gz" ln -sf /opt/readium/readium /usr/local/bin/readium - fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" msg_info "Setting up Storyteller" cd /opt/storyteller -export NODE_OPTIONS="--max-old-space-size=4096" $STD yarn install --network-timeout 600000 $STD gcc -g -fPIC -rdynamic -shared web/sqlite/uuid.c -o web/sqlite/uuid.c.so STORYTELLER_SECRET_KEY=$(openssl rand -base64 32) diff --git a/misc/tools.func b/misc/tools.func index 0e3fd0c2..3bdfbea5 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8681,6 +8681,233 @@ EOF return 0 } +# ------------------------------------------------------------------------------ +# Get latest GitLab release version. +# Usage: get_latest_gitlab_release "owner/repo" [strip_v] +# ------------------------------------------------------------------------------ +get_latest_gitlab_release() { + local repo="$1" + local strip_v="${2:-true}" + + local repo_encoded + repo_encoded=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$repo" 2>/dev/null || + echo "$repo" | sed 's|/|%2F|g') + + local header=() + [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") + + local temp_file + temp_file=$(mktemp) + + local http_code + http_code=$(curl --connect-timeout 10 --max-time 30 -sSL \ + -w "%{http_code}" -o "$temp_file" \ + "${header[@]}" \ + "https://gitlab.com/api/v4/projects/$repo_encoded/releases?per_page=1&order_by=released_at&sort=desc" 2>/dev/null) || true + + if [[ "$http_code" != "200" ]]; then + rm -f "$temp_file" + msg_warn "GitLab API call failed for ${repo} (HTTP ${http_code})" + return 22 + fi + + local version + version=$(jq -r '.[0].tag_name // empty' "$temp_file") + rm -f "$temp_file" + + if [[ -z "$version" ]]; then + msg_error "Could not determine latest version for ${repo}" + return 250 + fi + + if [[ "$strip_v" == "true" ]]; then + [[ "$version" =~ ^v[0-9] ]] && version="${version:1}" + fi + + echo "$version" +} + +# ------------------------------------------------------------------------------ +# Checks for new GitLab release (latest tag). +# +# Description: +# - Queries the GitLab API for the latest release tag +# - Compares it to a local cached version (~/.) +# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 +# +# Usage: +# if check_for_gl_release "myapp" "owner/repo" [optional] "v1.2.3"; then +# # trigger update... +# fi +# exit 0 +# } (end of update_script not from the function) +# +# Notes: +# - Requires `jq` (auto-installed if missing) +# - Supports GITLAB_TOKEN env var for private/rate-limited repos +# - Does not modify anything, only checks version state +# ------------------------------------------------------------------------------ +check_for_gl_release() { + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local pin_reason="${4:-}" # optional reason shown to user + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" + + msg_info "Checking for update: ${app}" + + # DNS check + if ! getent hosts gitlab.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve gitlab.com" + return 6 + fi + + ensure_dependencies jq + + local repo_encoded + repo_encoded=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$source" 2>/dev/null || + echo "$source" | sed 's|/|%2F|g') + + local header=() + [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") + + local releases_json="" http_code="" + + # For pinned versions, try to fetch the specific release tag first + if [[ -n "$pinned_version_in" ]]; then + local pinned_encoded="${pinned_version_in//\//%2F}" + http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o /tmp/gl_check.json \ + "${header[@]}" \ + "https://gitlab.com/api/v4/projects/$repo_encoded/releases/$pinned_encoded" 2>/dev/null) || true + if [[ "$http_code" == "200" ]] && [[ -s /tmp/gl_check.json ]]; then + releases_json="[$(/dev/null) || true + + if [[ "$http_code" == "200" ]] && [[ -s /tmp/gl_check.json ]]; then + releases_json=$(/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" + fi + fi + if [[ "$current" =~ ^v[0-9] ]]; then + current="${current:1}" + fi + + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean + if [[ "$pinned_version_in" =~ ^v[0-9] ]]; then + pin_clean="${pinned_version_in:1}" + else + pin_clean="$pinned_version_in" + fi + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break + fi + done + + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" + return 250 + fi + + if [[ "$current" != "$pin_clean" ]]; then + CHECK_UPDATE_RELEASE="$match_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" + return 0 + fi + + if [[ -n "$pin_reason" ]]; then + msg_ok "No update available: ${app} (${current}) - update held back: ${pin_reason}" + else + msg_ok "No update available: ${app} (${current}) - update temporarily held back due to issues with newer releases" + fi + return 1 + fi + + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" + return 0 + fi + + msg_ok "No update available: ${app} (${latest_clean})" + return 1 +} + function fetch_and_deploy_gl_release() { local app="$1" local repo="$2"