#!/usr/bin/env bash # # stage-mobile-update.sh # ---------------------- # Publishes the LogShip Mobile Android update artifacts into the logyou.de # website project (../logship/logyou-landing, Nuxt 4), which serves them as # static files under https://www.logyou.de/logship/ — the URL the in-app # updater reads (see app/composables/useAppUpdate.ts and # server/api/mobile/app-version.get.ts). Since the website relaunch the old # SFTP web root no longer exists; deploying the website repo IS the release. # # android/update-files/logship-mobile.apk -> /public/logship/logship-mobile.apk # android/update-files/version.json -> /public/logship/version.json # (replaced APK) -> /public/logship/history/logship-mobile--.apk # history.json -> merged manifest for the in-app version browser # # NOTE: this is the LEGACY channel kept for devices on old builds. New builds are served from the # ERP itself (scripts/publish-mobile-update.sh → data/mobile-releases/ on app.logship.de). # # The layout and the archive/history.json logic mirror scripts/upload-mobile-update.sh # (which is still used for SFTP-hosted targets such as Blut24). # # Usage: # scripts/stage-mobile-update.sh # copy + archive, then print the git commands # scripts/stage-mobile-update.sh --commit # additionally git-commit the files in the landing repo # scripts/stage-mobile-update.sh --push # --commit + git push (= production deploy via webhook) # # Env: # LANDING_DIR path of the logyou-landing checkout (default ../logship/logyou-landing) # KEEP_HISTORY how many archived APKs to keep in history/ (default 10; 0 = keep all) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" UPDATE_DIR="$REPO_ROOT/android/update-files" APK_LOCAL="$UPDATE_DIR/logship-mobile.apk" VERSION_LOCAL="$UPDATE_DIR/version.json" LANDING_DIR="${LANDING_DIR:-$REPO_ROOT/../logship/logyou-landing}" KEEP_HISTORY="${KEEP_HISTORY:-10}" TARGET_REL="public/logship" ARCHIVE_SUBDIR="history" bold() { printf '\033[1m%s\033[0m\n' "$*"; } err() { printf '\033[31mERROR:\033[0m %s\n' "$*" >&2; } ok() { printf '\033[32m✔\033[0m %s\n' "$*"; } DO_COMMIT=0; DO_PUSH=0 for a in "$@"; do case "$a" in --commit) DO_COMMIT=1 ;; --push) DO_COMMIT=1; DO_PUSH=1 ;; -h|--help) sed -n '3,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) err "unknown option: $a"; exit 1 ;; esac done # -------------------------------------------------------------------------- # Validate # -------------------------------------------------------------------------- for f in "$APK_LOCAL" "$VERSION_LOCAL"; do [ -f "$f" ] || { err "Missing $f — run scripts/release-app.sh (or stage the APK) first"; exit 1; } done if [ ! -d "$LANDING_DIR/public" ] || [ ! -f "$LANDING_DIR/nuxt.config.ts" ]; then err "logyou-landing project not found at $LANDING_DIR (set LANDING_DIR)"; exit 1 fi LANDING_DIR="$(cd "$LANDING_DIR" && pwd)" TARGET_DIR="$LANDING_DIR/$TARGET_REL" mkdir -p "$TARGET_DIR/$ARCHIVE_SUBDIR" json_field() { # file key -> value (string or number) node -e 'const j=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));process.stdout.write(String(j[process.argv[2]]??""))' "$1" "$2" } new_vn="$(json_field "$VERSION_LOCAL" versionName)" new_vc="$(json_field "$VERSION_LOCAL" versionCode)" [ -n "$new_vn" ] && [ -n "$new_vc" ] || { err "versionName/versionCode missing in $VERSION_LOCAL"; exit 1; } # APK ↔ version.json sanity check (the updater keys off version.json) find_aapt() { local base cand for base in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}" "$HOME/Library/Android/sdk" "$HOME/Android/Sdk"; do [ -n "$base" ] || continue cand="$(ls -1 "$base"/build-tools/*/aapt 2>/dev/null | sort -V | tail -1 || true)" [ -n "$cand" ] && { printf '%s' "$cand"; return 0; } done command -v aapt >/dev/null 2>&1 && { command -v aapt; return 0; } return 1 } AAPT="$(find_aapt || true)" if [ -n "${AAPT:-}" ]; then badging="$("$AAPT" dump badging "$APK_LOCAL" 2>/dev/null | grep '^package:' || true)" apk_vn="$(printf '%s' "$badging" | sed -n "s/.*versionName='\([^']*\)'.*/\1/p")" apk_vc="$(printf '%s' "$badging" | sed -n "s/.*versionCode='\([^']*\)'.*/\1/p")" bold "Version check" printf ' APK : versionName=%s versionCode=%s\n' "$apk_vn" "$apk_vc" printf ' version.json: versionName=%s versionCode=%s\n' "$new_vn" "$new_vc" if [ "$apk_vn" != "$new_vn" ] || [ "$apk_vc" != "$new_vc" ]; then err "version.json does NOT match the APK — devices compare version.json, a mismatch breaks updates." read -rp "Stage anyway? [y/N] " ans case "$ans" in y|Y) ;; *) exit 1;; esac else ok "version.json matches the APK" fi else printf 'aapt not found — skipping APK/version.json match check.\n' fi # -------------------------------------------------------------------------- # Archive the currently published version (if any, and if different) # -------------------------------------------------------------------------- old_vn=""; old_vc=""; ARCHIVE_NAME="" if [ -f "$TARGET_DIR/version.json" ]; then old_vn="$(json_field "$TARGET_DIR/version.json" versionName)" old_vc="$(json_field "$TARGET_DIR/version.json" versionCode)" fi if [ -n "$old_vn" ] && [ -n "$old_vc" ] && [ "$old_vc" != "$new_vc" ] && [ -f "$TARGET_DIR/logship-mobile.apk" ]; then ARCHIVE_NAME="logship-mobile-${old_vn}-${old_vc}.apk" fi bold "About to stage" printf ' from : %s\n %s\n' "$APK_LOCAL" "$VERSION_LOCAL" printf ' to : %s\n' "$TARGET_DIR" printf ' version: %s (code %s)' "$new_vn" "$new_vc" [ -n "$old_vc" ] && printf ' replacing %s (code %s)' "$old_vn" "$old_vc" echo if [ -n "$ARCHIVE_NAME" ]; then printf ' archive: current APK -> %s/%s (keep last %s)\n' "$ARCHIVE_SUBDIR" "$ARCHIVE_NAME" "$([ "$KEEP_HISTORY" = "0" ] && echo all || echo "$KEEP_HISTORY")" else printf ' archive: (none — first release or same version)\n' fi if [ -n "$ARCHIVE_NAME" ]; then mv -f "$TARGET_DIR/logship-mobile.apk" "$TARGET_DIR/$ARCHIVE_SUBDIR/$ARCHIVE_NAME" # Merge the replaced version into history.json (newest first, dedup by versionCode), # prune to KEEP_HISTORY entries and delete archived APKs no longer listed. OLD_VERSION_FILE="$TARGET_DIR/version.json" HISTORY_FILE="$TARGET_DIR/history.json" \ ARCHIVE_URL="${ARCHIVE_SUBDIR}/${ARCHIVE_NAME}" KEEP="$KEEP_HISTORY" ARCHIVE_DIR="$TARGET_DIR/$ARCHIVE_SUBDIR" \ node -e ' const fs = require("fs"), path = require("path"); const oldV = JSON.parse(fs.readFileSync(process.env.OLD_VERSION_FILE, "utf8")); let hist = []; try { hist = JSON.parse(fs.readFileSync(process.env.HISTORY_FILE, "utf8")); if (!Array.isArray(hist)) hist = []; } catch { hist = []; } const entry = { versionName: oldV.versionName || "", versionCode: Number(oldV.versionCode) || 0, releaseDate: oldV.releaseDate || "", releaseNotes: oldV.releaseNotes || "", apkUrl: process.env.ARCHIVE_URL, }; hist = hist.filter(h => Number(h.versionCode) !== entry.versionCode); hist.unshift(entry); hist.sort((a, b) => (Number(b.versionCode) || 0) - (Number(a.versionCode) || 0)); const keep = Number(process.env.KEEP) || 0; const dropped = keep > 0 ? hist.splice(keep) : []; for (const d of dropped) { const f = path.join(process.env.ARCHIVE_DIR, path.basename(String(d.apkUrl || ""))); if (d.apkUrl && fs.existsSync(f)) { fs.unlinkSync(f); console.log(" pruned " + path.basename(f)); } } fs.writeFileSync(process.env.HISTORY_FILE, JSON.stringify(hist, null, 2) + "\n"); console.log(" history.json: " + hist.length + " entries"); ' ok "archived previous APK as $ARCHIVE_SUBDIR/$ARCHIVE_NAME" fi cp -f "$APK_LOCAL" "$TARGET_DIR/logship-mobile.apk" cp -f "$VERSION_LOCAL" "$TARGET_DIR/version.json" [ -f "$TARGET_DIR/history.json" ] || printf '[]\n' > "$TARGET_DIR/history.json" ok "staged $TARGET_REL/logship-mobile.apk ($(du -h "$TARGET_DIR/logship-mobile.apk" | cut -f1)) + version.json" # -------------------------------------------------------------------------- # Git (the website deploys from its repo — pushing main = release) # -------------------------------------------------------------------------- cd "$LANDING_DIR" MSG="Mobile app update ${new_vn} (code ${new_vc})" if [ "$DO_COMMIT" = "1" ]; then git add -A -- "$TARGET_REL" if git diff --cached --quiet; then ok "nothing to commit in $TARGET_REL" else git commit -q -m "$MSG" -- "$TARGET_REL" ok "committed: $MSG" fi if [ "$DO_PUSH" = "1" ]; then git push ok "pushed — the website deploy publishes https://www.logyou.de/logship/version.json" fi else echo bold "Next: commit + deploy the website" printf ' cd %s\n git add -A -- %s\n git commit -m "%s"\n git push # webhook deploy of logyou.de\n' "$LANDING_DIR" "$TARGET_REL" "$MSG" fi