From 257a65633aafb74c930c21c3e8bc61f76a790f23 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 18 Feb 2026 13:07:35 +0100 Subject: [PATCH 1/6] Housekeeping --- BETA_TEST_PLAN.md | 139 ---------------------------------------------- 1 file changed, 139 deletions(-) delete mode 100644 BETA_TEST_PLAN.md diff --git a/BETA_TEST_PLAN.md b/BETA_TEST_PLAN.md deleted file mode 100644 index 62e679345..000000000 --- a/BETA_TEST_PLAN.md +++ /dev/null @@ -1,139 +0,0 @@ -# Beta Test Plan — Spool Inventory & Related Features - -## Prerequisites - -- At least one printer connected with AMS -- At least one Bambu Lab spool (RFID) loaded in AMS -- At least one spool without RFID tag -- At least one empty AMS slot (or slot with non-BL filament) -- A 3MF file ready to print (small/fast test print recommended) - ---- - -## 1. Filament Tracking Mode Switching - -**Location:** Settings > Filament - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 1.1 | Default mode | Open Settings > Filament | "Built-in Inventory" card is selected (green border), info panel shows RFID/usage/catalog bullet points | -| 1.2 | Switch to Spoolman | Click "Spoolman" card | Spoolman config appears (URL input, Sync Mode, connection status). Built-in info panel disappears | -| 1.3 | Switch back | Click "Built-in Inventory" card | Spoolman config disappears, built-in info panel reappears | -| 1.4 | Persistence | Switch mode, reload page | Selected mode persists after reload | -| 1.5 | Spoolman disabled state | Select Spoolman without URL, check Connect button | Connect button should be disabled when URL is empty | - ---- - -## 2. Spool Management - -**Location:** Inventory page (sidebar) - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 2.1 | Add spool | Click "+ Add Spool", fill material/brand/color/weight, save | Spool appears in table with correct info | -| 2.2 | Edit spool | Click a spool row, change fields, save | Changes reflected in table | -| 2.3 | Remaining weight | Edit spool > Additional section > adjust remaining weight | Remaining weight = label_weight - weight_used, slider/input updates correctly | -| 2.4 | Spool catalog | Edit spool > Additional > Empty Spool Weight dropdown | Pre-defined spool weights shown, selecting one updates the field | -| 2.5 | Color picker | Edit spool > Color section | Recent colors, brand palettes, and hex input all work | -| 2.6 | PA profile tab | Edit spool > PA Profile tab | Matching K-profiles shown grouped by printer/nozzle (requires calibration data) | -| 2.7 | Archive spool | Archive a spool from context menu or edit | Spool moves to "Archived" tab | -| 2.8 | Delete spool | Delete a spool | Spool removed from all views | -| 2.9 | Summary cards | Check top of inventory page | Total Inventory, Total Consumed, By Material, In Printer, Low Stock cards show correct values | -| 2.10 | Filters | Try Active/Archived/All tabs, material/brand dropdowns, search | Filtering works correctly | -| 2.11 | View modes | Toggle between Table and Card view | Both views show correct spool data | - ---- - -## 3. AMS Slot Assignment - -**Location:** Printers page, AMS hover cards - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 3.1 | Assign spool | Hover over an empty/non-BL AMS slot > click "Assign Spool" | Modal opens showing only manual (non-BL) spools | -| 3.2 | BL spools filtered | Open assign modal | Bambu Lab spools (with RFID tags) are NOT in the list | -| 3.3 | Assigned spools filtered | Assign spool A to slot 1, then open assign modal for slot 2 | Spool A is NOT in the list (already assigned) | -| 3.4 | Current slot spool visible | Open assign for slot that already has a spool | The currently assigned spool IS still shown (for reassignment) | -| 3.5 | Confirm assignment | Select spool, click "Assign Spool" | Slot shows the assigned spool info on hover | -| 3.6 | Unassign spool | Hover over assigned slot > click "Unassign" | Spool removed from slot, slot shows default info | -| 3.7 | BL slot — no buttons | Hover over a slot with a Bambu Lab spool (RFID) | No "Assign Spool" or "Unassign" buttons shown | -| 3.8 | Empty slot display | Check an empty AMS slot | Shows type from AMS data or "Empty" (localized) | - ---- - -## 4. Auto-Unlink on BL Spool Insertion - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 4.1 | BL spool replaces manual | Assign a manual spool to a slot, then physically insert a BL spool into that slot | Assignment automatically removed, slot now shows BL spool info | -| 4.2 | Log message | Check backend logs after 4.1 | Log shows "Auto-unlink: spool X AMSY-TZ — Bambu Lab spool detected" | - ---- - -## 5. Spool Tag Linking - -**Location:** Printers page, AMS hover cards for BL spools - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 5.1 | Link modal | Hover over BL spool slot > click "Link to Spool" (if available) | Modal opens showing only untagged spools | -| 5.2 | Search filter | Type in search box | Spools filtered by material/brand/color | -| 5.3 | Link spool | Click a spool in the list | Success toast, modal closes, spool now linked to tag | -| 5.4 | Tagged spools hidden | After linking spool A, open link modal again | Spool A is no longer in the list | - ---- - -## 6. Usage Tracking — BL Spools (AMS Remain%) - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 6.1 | Session capture | Start a print with a BL spool | Backend log shows "Captured start remain% for printer X (N trays)" | -| 6.2 | Completed print | Let print finish | Spool's weight_used increases by (delta% * label_weight). Check inventory page remaining weight | -| 6.3 | Failed/aborted print | Start and cancel a print mid-way | Usage still tracked based on remain% delta at time of stop | -| 6.4 | No double-tracking | Complete a print with BL spool | Only AMS delta tracking applies (no 3MF fallback for this spool) | - ---- - -## 7. Usage Tracking — Non-BL Spools (3MF Estimates) - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 7.1 | Assign and print | Assign a manual spool to AMS slot, start a print using that slot | Backend log shows "3MF fallback available" at print start | -| 7.2 | Completed print | Let print finish | Spool's weight_used increases by the 3MF estimated used_g. Check inventory page | -| 7.3 | Failed print scaling | Start a print, cancel at ~50% | Usage = 3MF estimate * (progress/100). E.g., 100g estimate at 50% = ~50g tracked | -| 7.4 | Multi-slot print | Print using multiple filament slots (some BL, some manual) | BL spools tracked via remain% delta, manual spools via 3MF. No double-counting | -| 7.5 | Slot mapping | Use a spool in AMS slot 5 (second AMS unit) | Correctly maps to AMS 1, Tray 0. Check usage history shows correct ams_id/tray_id | - ---- - -## 8. Edge Cases - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 8.1 | No AMS data | Print from external spool (no AMS) | No crash, usage tracking gracefully skipped | -| 8.2 | No archive | Print without 3MF archive available | AMS delta path still works for BL spools, 3MF path skipped gracefully | -| 8.3 | Spool refilled | If remain% goes UP between start and end (spool swapped/refilled) | Negative delta skipped, no negative usage recorded | -| 8.4 | Rapid print start/stop | Start and immediately cancel a print | No errors, minimal or zero usage tracked | -| 8.5 | Concurrent printers | Print on two printers simultaneously | Each printer tracked independently, no cross-contamination | - ---- - -## 9. UI/UX Checks - -| # | Test | Steps | Expected | -|---|------|-------|----------| -| 9.1 | Mobile layout | Open on mobile or narrow browser | Inventory page, assign modal, and settings all responsive | -| 9.2 | Dark/light mode | Toggle theme | All inventory UI elements properly themed | -| 9.3 | Language switching | Switch to German and Japanese | All inventory strings translated (no hardcoded English) | -| 9.4 | Keyboard nav | Use keyboard shortcuts on printers page | AMS slot interactions accessible | - ---- - -## Reporting Issues - -When reporting a bug, please include: -- Test case number (e.g., "Beta 9.1 failed") -- Browser + version -- Screenshot or screen recording -- Backend logs (if relevant — check Docker logs) -- Steps to reproduce if different from above From 22ac7e97bb17e30edfa72afcbe76689538767ab0 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 18 Feb 2026 11:05:16 +0100 Subject: [PATCH 2/6] Closes #426 --- frontend/.npmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 frontend/.npmrc diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 000000000..dc0b3a412 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1 @@ +audit-level=high From 101288d1b22d866e6edbb9b4af675ff2c35e4b3f Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 18 Feb 2026 13:28:11 +0100 Subject: [PATCH 3/6] Updated CI --- .github/workflows/ci.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f74b0fd56..57c8a6bed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,7 +151,23 @@ jobs: - name: Run npm audit working-directory: frontend - run: npm audit --audit-level=high + run: | + # Only fail on fixable high/critical vulnerabilities. + # Unfixable issues (e.g. npm's bundled tar) are tracked via security.yml. + npm audit --json > /tmp/audit.json 2>/dev/null || true + python3 -c " + import json, sys + data = json.load(open('/tmp/audit.json')) + vulns = data.get('vulnerabilities', {}) + fixable = {n: v for n, v in vulns.items() + if v.get('severity') in ('high', 'critical') and v.get('fixAvailable')} + if fixable: + for name, v in fixable.items(): + print(f'FIXABLE {v[\"severity\"].upper()}: {name}') + sys.exit(1) + total = sum(1 for v in vulns.values() if v.get('severity') in ('high', 'critical')) + print(f'npm audit: {total} high/critical (0 fixable), {len(vulns)} total known') + " frontend-typecheck: name: Frontend Type Check From d12aaa222eb73c8ce5f66cf4f2ea8ec70d3baf42 Mon Sep 17 00:00:00 2001 From: uefigs139 Date: Wed, 18 Feb 2026 08:32:04 -0500 Subject: [PATCH 4/6] feat: add native updater script with backup and safety checks --- install/README.md | 31 +++++++-- install/update.sh | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 6 deletions(-) create mode 100755 install/update.sh diff --git a/install/README.md b/install/README.md index fd1139fe6..43e7d178d 100644 --- a/install/README.md +++ b/install/README.md @@ -26,6 +26,7 @@ curl -fsSL https://raw.githubusercontent.com/maziggy/bambuddy/main/install/insta |--------|----------|--------| | `install.sh` | Linux, macOS | Native (Python venv) | | `docker-install.sh` | Linux, macOS | Docker | +| `update.sh` | Linux (systemd) | Native update helper | --- @@ -159,12 +160,30 @@ docker compose logs -f # View logs **Native installation:** ```bash -cd /opt/bambuddy -git pull -source venv/bin/activate -pip install -r requirements.txt -cd frontend && npm ci && npm run build -sudo systemctl restart bambuddy # Linux +curl -fsSL https://raw.githubusercontent.com/maziggy/bambuddy/main/install/update.sh -o update.sh +chmod +x update.sh +sudo ./update.sh +``` + +The updater performs: +- Root permission check (fails fast before any work) +- Optional built-in backup API call (`/api/v1/settings/backup`) before update +- Local-change warning + confirmation before `git reset --hard` +- Service stop/start with rollback attempt if update fails + +Useful environment overrides: +```bash +# Typical native install defaults +INSTALL_DIR=/opt/bambuddy SERVICE_NAME=bambuddy sudo ./update.sh + +# Require backup to succeed (abort update if backup fails) +BACKUP_MODE=require sudo ./update.sh + +# Skip backup API call +BACKUP_MODE=skip sudo ./update.sh + +# Auth-enabled instances: provide API key for backup endpoint +BAMBUDDY_API_KEY=bb_xxx BACKUP_MODE=require sudo ./update.sh ``` **Docker (pre-built image):** diff --git a/install/update.sh b/install/update.sh new file mode 100755 index 000000000..a50d10ba0 --- /dev/null +++ b/install/update.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +INSTALL_DIR="${INSTALL_DIR:-/opt/bambuddy}" +SERVICE_NAME="${SERVICE_NAME:-bambuddy}" +BRANCH="${BRANCH:-}" +VENV_PIP="${VENV_PIP:-$INSTALL_DIR/venv/bin/pip}" +FRONTEND_DIR="${FRONTEND_DIR:-$INSTALL_DIR/frontend}" +BACKUP_DIR="${BACKUP_DIR:-$INSTALL_DIR/backups}" +BAMBUDDY_API_URL="${BAMBUDDY_API_URL:-http://127.0.0.1:8000/api/v1}" +BAMBUDDY_API_KEY="${BAMBUDDY_API_KEY:-}" +BACKUP_MODE="${BACKUP_MODE:-auto}" # auto|require|skip +FORCE="${FORCE:-0}" + +SERVICE_STOPPED=0 + +log() { + printf '[bambuddy-update] %s\n' "$*" +} + +warn() { + printf '[bambuddy-update] WARNING: %s\n' "$*" >&2 +} + +die() { + printf '[bambuddy-update] ERROR: %s\n' "$*" >&2 + exit 1 +} + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +on_error() { + local exit_code="$1" + if [ "$SERVICE_STOPPED" -eq 1 ]; then + warn "Update failed, attempting to restart service: $SERVICE_NAME" + systemctl start "$SERVICE_NAME" || true + fi + exit "$exit_code" +} +trap 'on_error $?' ERR + +create_backup() { + local ts backup_file + + if [ "$BACKUP_MODE" = "skip" ]; then + log "Skipping backup (BACKUP_MODE=skip)" + return 0 + fi + + if ! systemctl is-active --quiet "$SERVICE_NAME"; then + if [ "$BACKUP_MODE" = "require" ]; then + die "Service is not running; cannot call built-in backup API." + fi + warn "Service is not running; skipping built-in backup API call." + return 0 + fi + + mkdir -p "$BACKUP_DIR" + ts="$(date +%Y%m%d-%H%M%S)" + backup_file="$BACKUP_DIR/bambuddy-backup-$ts.zip" + + log "Creating built-in backup via API: $backup_file" + if [ -n "$BAMBUDDY_API_KEY" ]; then + if curl --silent --show-error --fail --location \ + --connect-timeout 5 --max-time 900 \ + -H "X-API-Key: $BAMBUDDY_API_KEY" \ + "$BAMBUDDY_API_URL/settings/backup" \ + --output "$backup_file"; then + log "Backup created successfully" + return 0 + fi + else + if curl --silent --show-error --fail --location \ + --connect-timeout 5 --max-time 900 \ + "$BAMBUDDY_API_URL/settings/backup" \ + --output "$backup_file"; then + log "Backup created successfully" + return 0 + fi + fi + + rm -f "$backup_file" + if [ "$BACKUP_MODE" = "require" ]; then + die "Built-in backup API call failed (BACKUP_MODE=require)." + fi + warn "Built-in backup API call failed. Continuing because BACKUP_MODE=auto." +} + +[ "${EUID:-$(id -u)}" -eq 0 ] || die "Run as root (or with sudo)." + +case "$BACKUP_MODE" in + auto|require|skip) ;; + *) die "Invalid BACKUP_MODE '$BACKUP_MODE' (expected: auto, require, skip)." ;; +esac + +require_cmd git +require_cmd systemctl +require_cmd curl + +[ -d "$INSTALL_DIR" ] || die "Install directory not found: $INSTALL_DIR" +cd "$INSTALL_DIR" +[ -d .git ] || die "No git repository found in: $INSTALL_DIR" + +if [ -z "$BRANCH" ]; then + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + [ "$BRANCH" = "HEAD" ] && BRANCH="main" +fi + +if ! systemctl list-unit-files --type=service | grep -q "^${SERVICE_NAME}\.service"; then + die "Service not found: ${SERVICE_NAME}.service" +fi + +if [ -n "$(git status --porcelain)" ]; then + warn "Local changes detected in $INSTALL_DIR." + warn "This update uses: git reset --hard origin/$BRANCH" + if [ "$FORCE" != "1" ]; then + read -r -p "Discard local changes and continue? [y/N]: " answer + case "${answer:-}" in + y|Y|yes|YES) ;; + *) die "Update cancelled by user." ;; + esac + else + warn "Proceeding without prompt because FORCE=1." + fi +fi + +create_backup + +old_commit="$(git rev-parse --short HEAD || true)" + +log "Stopping service: $SERVICE_NAME" +systemctl stop "$SERVICE_NAME" +SERVICE_STOPPED=1 + +log "Updating code from origin/$BRANCH" +git fetch --prune origin +git reset --hard "origin/$BRANCH" + +if [ -x "$VENV_PIP" ] && [ -f requirements.txt ]; then + log "Updating Python dependencies" + "$VENV_PIP" install -r requirements.txt +else + warn "Skipping Python dependency update (venv pip or requirements.txt missing)." +fi + +if [ -f "$FRONTEND_DIR/package.json" ]; then + if command -v npm >/dev/null 2>&1; then + log "Building frontend" + ( + cd "$FRONTEND_DIR" + npm ci + npm run build + ) + else + warn "Skipping frontend build (npm not installed)." + fi +else + warn "Skipping frontend build (frontend/package.json not found)." +fi + +log "Starting service: $SERVICE_NAME" +systemctl start "$SERVICE_NAME" +SERVICE_STOPPED=0 +systemctl --no-pager --lines=8 status "$SERVICE_NAME" + +new_commit="$(git rev-parse --short HEAD || true)" +log "Update complete: ${old_commit:-unknown} -> ${new_commit:-unknown}" From 78f17614e735220ad733cdcc03f253a461ca6a10 Mon Sep 17 00:00:00 2001 From: uefigs139 Date: Wed, 18 Feb 2026 08:48:07 -0500 Subject: [PATCH 5/6] fix: use robust systemd service detection in updater --- install/update.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/update.sh b/install/update.sh index a50d10ba0..cea32ff21 100755 --- a/install/update.sh +++ b/install/update.sh @@ -108,7 +108,8 @@ if [ -z "$BRANCH" ]; then [ "$BRANCH" = "HEAD" ] && BRANCH="main" fi -if ! systemctl list-unit-files --type=service | grep -q "^${SERVICE_NAME}\.service"; then +load_state="$(systemctl show "$SERVICE_NAME" --property=LoadState --value 2>/dev/null || true)" +if [ -z "$load_state" ] || [ "$load_state" = "not-found" ]; then die "Service not found: ${SERVICE_NAME}.service" fi From 3b02d3eeddb689c049bab20cfe51f81581b6b76b Mon Sep 17 00:00:00 2001 From: uefigs139 Date: Wed, 18 Feb 2026 13:36:45 -0500 Subject: [PATCH 6/6] Updated prompt flow of update --- install/README.md | 4 ++- install/update.sh | 90 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/install/README.md b/install/README.md index 43e7d178d..443810db6 100644 --- a/install/README.md +++ b/install/README.md @@ -168,8 +168,10 @@ sudo ./update.sh The updater performs: - Root permission check (fails fast before any work) - Optional built-in backup API call (`/api/v1/settings/backup`) before update +- Keeps only the newest 5 local backup ZIP files - Local-change warning + confirmation before `git reset --hard` -- Service stop/start with rollback attempt if update fails +- If remote has no new commits, updater exits early without stopping the service +- Service stop/start with code rollback + service restart attempt if update fails Useful environment overrides: ```bash diff --git a/install/update.sh b/install/update.sh index cea32ff21..6df52955f 100755 --- a/install/update.sh +++ b/install/update.sh @@ -10,9 +10,12 @@ BACKUP_DIR="${BACKUP_DIR:-$INSTALL_DIR/backups}" BAMBUDDY_API_URL="${BAMBUDDY_API_URL:-http://127.0.0.1:8000/api/v1}" BAMBUDDY_API_KEY="${BAMBUDDY_API_KEY:-}" BACKUP_MODE="${BACKUP_MODE:-auto}" # auto|require|skip +BACKUP_KEEP_COUNT=5 FORCE="${FORCE:-0}" SERVICE_STOPPED=0 +CODE_UPDATED=0 +old_commit="" log() { printf '[bambuddy-update] %s\n' "$*" @@ -31,18 +34,44 @@ require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" } +cleanup_old_backups() { + local -a backup_files + local max_count="$1" + + [ "$max_count" -gt 0 ] || return 0 + + mapfile -t backup_files < <(ls -1t "$BACKUP_DIR"/bambuddy-backup-*.zip 2>/dev/null || true) + if [ "${#backup_files[@]}" -le "$max_count" ]; then + return 0 + fi + + for old_file in "${backup_files[@]:$max_count}"; do + rm -f "$old_file" + done + + log "Pruned old backups, kept newest $max_count file(s)" +} + on_error() { local exit_code="$1" + if [ "$SERVICE_STOPPED" -eq 1 ]; then + if [ "$CODE_UPDATED" -eq 1 ] && [ -n "$old_commit" ]; then + warn "Update failed after code change, attempting rollback to $old_commit" + git reset --hard "$old_commit" || warn "Rollback reset failed" + fi + warn "Update failed, attempting to restart service: $SERVICE_NAME" systemctl start "$SERVICE_NAME" || true fi + exit "$exit_code" } trap 'on_error $?' ERR create_backup() { local ts backup_file + local -a auth_args=() if [ "$BACKUP_MODE" = "skip" ]; then log "Skipping backup (BACKUP_MODE=skip)" @@ -61,24 +90,17 @@ create_backup() { ts="$(date +%Y%m%d-%H%M%S)" backup_file="$BACKUP_DIR/bambuddy-backup-$ts.zip" + [ -n "$BAMBUDDY_API_KEY" ] && auth_args=(-H "X-API-Key: $BAMBUDDY_API_KEY") + log "Creating built-in backup via API: $backup_file" - if [ -n "$BAMBUDDY_API_KEY" ]; then - if curl --silent --show-error --fail --location \ - --connect-timeout 5 --max-time 900 \ - -H "X-API-Key: $BAMBUDDY_API_KEY" \ - "$BAMBUDDY_API_URL/settings/backup" \ - --output "$backup_file"; then - log "Backup created successfully" - return 0 - fi - else - if curl --silent --show-error --fail --location \ - --connect-timeout 5 --max-time 900 \ - "$BAMBUDDY_API_URL/settings/backup" \ - --output "$backup_file"; then - log "Backup created successfully" - return 0 - fi + if curl --silent --show-error --fail --location \ + --connect-timeout 5 --max-time 900 \ + "${auth_args[@]}" \ + "$BAMBUDDY_API_URL/settings/backup" \ + --output "$backup_file"; then + log "Backup created successfully" + cleanup_old_backups "$BACKUP_KEEP_COUNT" + return 0 fi rm -f "$backup_file" @@ -113,11 +135,33 @@ if [ -z "$load_state" ] || [ "$load_state" = "not-found" ]; then die "Service not found: ${SERVICE_NAME}.service" fi +old_commit="$(git rev-parse --short HEAD || true)" + +log "Fetching latest code from origin/$BRANCH" +git fetch --prune origin + +remote_commit="$(git rev-parse --short "origin/$BRANCH" || true)" +log "Current commit: ${old_commit:-unknown}" +log "Remote commit: ${remote_commit:-unknown}" + +if git diff --quiet HEAD "origin/$BRANCH"; then + log "You are already running the latest version of Bambuddy." + read -r -p "Do you want to run the update process anyway? [y/N]: " run_anyway + case "${run_anyway:-}" in + y|Y|yes|YES) ;; + *) exit 0 ;; + esac +else + read -r -p "An update for Bambuddy is available. Install now? [y/N]: " install_now + case "${install_now:-}" in + y|Y|yes|YES) ;; + *) exit 0 ;; + esac +fi + if [ -n "$(git status --porcelain)" ]; then - warn "Local changes detected in $INSTALL_DIR." - warn "This update uses: git reset --hard origin/$BRANCH" if [ "$FORCE" != "1" ]; then - read -r -p "Discard local changes and continue? [y/N]: " answer + read -r -p "Local edits were detected in your installation. Updating now will overwrite those edits. Continue? [y/N]: " answer case "${answer:-}" in y|Y|yes|YES) ;; *) die "Update cancelled by user." ;; @@ -129,15 +173,13 @@ fi create_backup -old_commit="$(git rev-parse --short HEAD || true)" - log "Stopping service: $SERVICE_NAME" systemctl stop "$SERVICE_NAME" SERVICE_STOPPED=1 -log "Updating code from origin/$BRANCH" -git fetch --prune origin +log "Updating code to origin/$BRANCH" git reset --hard "origin/$BRANCH" +CODE_UPDATED=1 if [ -x "$VENV_PIP" ] && [ -f requirements.txt ]; then log "Updating Python dependencies"