Files
Nexus-Guard-Suite/.sisyphus/plans/optimize-update-sh.md
T
datadunia cf6ffba404
NexusGuard CI / server-core-test (push) Failing after 36s
NexusGuard CI / device-agent-test (push) Failing after 31s
NexusGuard CI / dashboard-ui-build (push) Failing after 25s
chore: update plan docs, add optimize-update-sh plan
2026-05-27 04:36:53 +07:00

9.1 KiB

Optimize update.sh — Conditional Rebuild Only When Needed

TL;DR

Quick Summary: Modify update.sh to skip Docker rebuild, restart, and migration when no git/submodule/.env changes are detected. Prevents unnecessary 2-5 minute downtime on every run.

Deliverables:

  • update.sh — refactored with conditional rebuild logic
  • .update-state — persistent state file (gitignored)
  • .gitignore — add .update-state entry

Estimated Effort: Quick Parallel Execution: N/A (single file) Critical Path: N/A


Context

Original Request

User noticed ./update.sh always runs docker compose down, docker compose build, docker compose up -d, and migration — even when no code changes exist. This wastes time (2-5 min) and causes unnecessary downtime.

Metis Review — Key Findings

Critical Gap #1: .env changes (especially VITE_API_BASE_URL which is a --build-arg) are NOT tracked by git. Must hash .env content alongside git state.

Critical Gap #2: State file location must be .gitignore'd. Use ./.update-state with atomic write (tmp + mv).

Critical Gap #3: No force-rebuild mechanism. Must add --force flag.

Minor Gap #4: md5sum not portable to macOS. Use openssl sha256.

Minor Gap #5: On git pull or submodule failure, should ALWAYS rebuild (safe fallback).


Work Objectives

Core Objective

Skip Docker rebuild/restart/migration cycle when git state, submodule state, and .env are unchanged from last successful update.

Concrete Deliverables

  • update.sh — refactored with state comparison + conditional rebuild
  • .update-state — persistent state file (auto-created, never committed)
  • .gitignore — add .update-state entry

Definition of Done

  • Second consecutive run with no changes prints "No changes detected. Skipping." and exits in <5s
  • First run (or after any change) executes full cycle (pull, build, up, migrate)
  • bash update.sh --force always executes full cycle
  • .env change (esp. VITE_API_BASE_URL) triggers rebuild even without git change
  • Git pull failure triggers rebuild (safe fallback)
  • Corrupted state file treated as first run → always builds

Must Have

  • Conditional rebuild: only when git HEAD, submodules, or .env changed
  • --force flag to bypass state check
  • .update-state properly gitignored
  • Clean output: clear [+] / [-] indicators for skip vs rebuild paths

Must NOT Have (Guardrails)

  • Do NOT change the down → build → up cycle pattern when rebuild IS needed
  • Do NOT add per-submodule selective build (always build all or nothing)
  • Do NOT add Docker health-check polling or auto-rollback
  • Do NOT modify any file other than update.sh and .gitignore
  • Do NOT use docker-compose (v1) anywhere

Verification Strategy

ZERO HUMAN INTERVENTION — ALL verification is agent-executed.

Test Decision

  • Infrastructure exists: YES (bash on Linux server)
  • Automated tests: None (shell script test suite doesn't exist)
  • Primary verification: Run on remote server, verify behavior with:
    • ssh root@172.20.8.191 — execute updated script
    • First run: full cycle
    • Second run (no changes): skip
    • After .env edit: rebuild
    • With --force: rebuild

Execution Strategy

Single task, no waves needed — one file change.


TODOs

All tasks completed in commit 0b943ae

  • 1. Refactor update.sh — Add State Comparison & Conditional Rebuild Logic

    What to do:

    • Add at top of script (after set -e): define STATE_FILE=".update-state" path
    • After git pull + git submodule update --init --recursive --remote:
      1. Compute combined hash: CURRENT_HASH=$(echo "$(git rev-parse HEAD)$(git submodule status)$(sha256sum .env)" | sha256sum | cut -d' ' -f1)
      2. Read previous hash from $STATE_FILE (if exists)
      3. If $CURRENT_HASH matches previous AND --force not passed → skip rebuild
      4. Otherwise → execute full down → build → up -d → migrate → backfill cycle
    • Write new hash atomically: echo "$CURRENT_HASH" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
    • Handle --force flag: if [ "$1" = "--force" ]; then ...
    • Handle missing/corrupt state file (treat as first run → build)
    • Handle git pull failure (always build as safe fallback)
    • Handle git submodule update failure (always build as safe fallback)
    • Print clear [+]/[-] output for skip vs rebuild paths

    Must NOT do:

    • Do NOT change the down → build → up cycle pattern (preserve existing)
    • Do NOT add per-service selective build
    • Do NOT modify any existing command flags or environment sourcing

    Recommended Agent Profile:

    • Category: quick
      • Reason: Single file, well-defined logic, no external dependencies
    • Skills: []

    Parallelization:

    • Can Run In Parallel: N/A (single task)

    References:

    • update.sh — Current file to refactor (52 lines)
    • .gitignore:41connect_remote.txt already listed; add .update-state nearby

    Acceptance Criteria:

    • Script runs full cycle on first invocation (no .update-state)
    • Script skips full cycle on second invocation (no changes)
    • bash update.sh --force always runs full cycle
    • .env change triggers rebuild (hash detects difference)
    • Corrupted .update-state treated as first run
    • git pull failure → rebuild triggered (safe fallback)
    • All output clear and actionable

    QA Scenarios:

    Scenario A: Second run skips rebuild (no changes) ✅
      Tool: Bash (interactive_bash via tmux on server)
      Preconditions: State file created with correct hash
      Steps:
        1. ssh root@172.20.8.191
        2. cd /root/Nexus-Guard-Suite
        3. hash=$(echo "$(git rev-parse HEAD)$(git submodule status)$(sha256sum .env)" | sha256sum | cut -d' ' -f1)
        4. echo "$hash" > .update-state
        5. bash update.sh
      Result: "No changes detected. Skipping build and restart." in <2s, exit 0
      Evidence: .sisyphus/evidence/f1-update-sh-optimization.md
    
    Scenario B: --force triggers rebuild even without changes ✅
      Tool: Bash (interactive_bash via tmux on server)
      Preconditions: State file exists
      Steps:
        1. bash update.sh --force
      Result: "--force flag detected. Will rebuild." → full cycle
      Evidence: .sisyphus/evidence/f1-update-sh-optimization.md
    
    Scenario C: .env change triggers rebuild ✅
      Tool: Bash (interactive_bash via tmux on server)
      Preconditions: State file exists
      Steps:
        1. echo "# test change" >> .env
        2. bash update.sh
      Result: "State hash changed. Rebuilding." → full cycle
      Evidence: .sisyphus/evidence/f1-update-sh-optimization.md
    
    Scenario D: First run (no state) triggers rebuild ✅
      Tool: Bash (interactive_bash via tmux on server)
      Preconditions: No .update-state
      Steps:
        1. rm -f .update-state
        2. bash update.sh
      Result: "First run (no state file found). Full cycle required." → full cycle
      Evidence: .sisyphus/evidence/f1-update-sh-optimization.md
    

    Evidence to Capture:

    • Task 1 — skip-rebuild output
    • Task 1 — force-rebuild output
    • Task 1 — env-change rebuild output
    • Task 1 — corrupt-state output

    Evidence consolidated in .sisyphus/evidence/f1-update-sh-optimization.md

    Commit: YES

    • Message: chore(ops): optimize update.sh to skip rebuild when no changes detected
    • Files: update.sh, .gitignore
    • Pre-commit: review diff
  • 2. Add .update-state to .gitignore

    What to do:

    • Edit root .gitignore to add .update-state entry (alongside connect_remote.txt on line 41 or nearby)
    • This prevents accidental commit of machine-local build state

    Must NOT do:

    • Do NOT change any existing .gitignore entries
    • Do NOT add .update-state to submodule .gitignore files

    Recommended Agent Profile:

    • Category: quick
      • Reason: Trivial one-line addition
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES (independent of Task 1's logic, but logically grouped in same commit)
    • Blocked By: Commit groups with Task 1

    References:

    • .gitignore:41 — Current state, connect_remote.txt already listed there

    Acceptance Criteria:

    • git check-ignore .update-state returns the path (file is ignored)
    • No existing entries modified

    Evidence to Capture:

    • git check-ignore verification

    Commit: YES (group with Task 1)


Final Verification

  • F1. Behavioral Verification — Run on server across all scenarios

Commit Strategy

  • 1: chore(ops): optimize update.sh to skip rebuild when no changes detected

Success Criteria

# First run (or after changes): full cycle
bash update.sh
# Expected: git pull, down, build, up -d, migrate

# Second run (no changes): skip
bash update.sh
# Expected: "No changes detected. Skipping build and restart." in <5s

# Force rebuild
bash update.sh --force
# Expected: full cycle regardless

# After .env edit
bash update.sh
# Expected: rebuild detected (new .env hash)