9.1 KiB
Optimize update.sh — Conditional Rebuild Only When Needed
TL;DR
Quick Summary: Modify
update.shto skip Docker rebuild, restart, and migration when no git/submodule/.envchanges 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-stateentryEstimated 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-stateentry
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 --forcealways executes full cycle.envchange (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
.envchanged --forceflag to bypass state check.update-stateproperly gitignored- Clean output: clear
[+]/[-]indicators for skip vs rebuild paths
Must NOT Have (Guardrails)
- Do NOT change the
down → build → upcycle 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.shand.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
.envedit: 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 LogicWhat to do:
- Add at top of script (after
set -e): defineSTATE_FILE=".update-state"path - After
git pull+git submodule update --init --recursive --remote:- Compute combined hash:
CURRENT_HASH=$(echo "$(git rev-parse HEAD)$(git submodule status)$(sha256sum .env)" | sha256sum | cut -d' ' -f1) - Read previous hash from
$STATE_FILE(if exists) - If
$CURRENT_HASHmatches previous AND--forcenot passed → skip rebuild - Otherwise → execute full
down → build → up -d → migrate → backfillcycle
- Compute combined hash:
- Write new hash atomically:
echo "$CURRENT_HASH" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE" - Handle
--forceflag:if [ "$1" = "--force" ]; then ... - Handle missing/corrupt state file (treat as first run → build)
- Handle
git pullfailure (always build as safe fallback) - Handle
git submodule updatefailure (always build as safe fallback) - Print clear
[+]/[-]output for skip vs rebuild paths
Must NOT do:
- Do NOT change the
down → build → upcycle 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:41—connect_remote.txtalready listed; add.update-statenearby
Acceptance Criteria:
- Script runs full cycle on first invocation (no
.update-state) - Script skips full cycle on second invocation (no changes)
bash update.sh --forcealways runs full cycle.envchange triggers rebuild (hash detects difference)- Corrupted
.update-statetreated as first run git pullfailure → 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.mdEvidence 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.mdCommit: YES
- Message:
chore(ops): optimize update.sh to skip rebuild when no changes detected - Files:
update.sh,.gitignore - Pre-commit: review diff
- Add at top of script (after
-
2. Add
.update-stateto.gitignoreWhat to do:
- Edit root
.gitignoreto add.update-stateentry (alongsideconnect_remote.txton line 41 or nearby) - This prevents accidental commit of machine-local build state
Must NOT do:
- Do NOT change any existing
.gitignoreentries - Do NOT add
.update-stateto submodule.gitignorefiles
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.txtalready listed there
Acceptance Criteria:
git check-ignore .update-statereturns the path (file is ignored)- No existing entries modified
Evidence to Capture:
- git check-ignore verification
Commit: YES (group with Task 1)
- Edit root
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)