4.2 KiB
Bootstrap CLI: Procedural Rollback System Design
1. Objective
Provide a robust rollback mechanism by dynamically generating an uninstallation command list during the installation process. This avoids external parsers, keeps dependencies low, and leverages native Bash execution. It also includes a stateful savepoint system to revert complex environments.
2. Core Concept: Dynamic Command Manifests
Instead of tracking state in data files (like JSON), the system procedurally builds a Command Manifest (~/.local/state/bootstrap/uninstallers/<tool>.cmds) as the installation progresses. Every helper action records its inverse command as an independent line in this manifest.
3. History & Savepoints (b fall and b rb)
To allow rolling back multiple installations or returning to a known good state, the system maintains a chronological History Log acting as a stack.
Location: ~/.local/state/bootstrap/history.log
A. Creating a Savepoint (b fall <name>)
The b fall command simply appends a marker to the history log.
echo "SAVEPOINT: $name" >> "$HOME/.local/state/bootstrap/history.log"
B. Tracking Installations
Whenever an installation successfully completes, the b CLI appends an install marker:
echo "INSTALL: nvim" >> "$HOME/.local/state/bootstrap/history.log"
Example History Log:
SAVEPOINT: init
INSTALL: rust
INSTALL: node
SAVEPOINT: dev_setup
INSTALL: yazi
INSTALL: nvim
C. Bare Rollback (b rb)
When b rb is executed without arguments, it rolls back the single most recent change:
- Reads the last line of the history log (e.g.,
INSTALL: nvim). - Executes the command manifest for
nvim. - Deletes the last line from the history log.
D. Savepoint Rollback (b rb <name>)
When b rb init is executed, it rolls back all changes made after that savepoint:
- Parses the history log from bottom to top.
- For each
INSTALL: <tool>encountered, it executes the rollback manifest for<tool>. - Stops when it reaches
SAVEPOINT: init. - Truncates the history log back to the savepoint.
4. Required Abstractions & Helper Modifications
A. Context Initialization
Before executing an installer script, the b CLI initializes the command list:
export BOOTSTRAP_UNINSTALLER_CMDS="$HOME/.local/state/bootstrap/uninstallers/nvim.cmds"
mkdir -p "$(dirname "$BOOTSTRAP_UNINSTALLER_CMDS")"
touch "$BOOTSTRAP_UNINSTALLER_CMDS"
B. Recording Commands (LIFO Execution)
Rollback steps are safest when executed in reverse order. A helper prepends commands to the top of the manifest.
add_rollback_cmd() {
local cmd="$1"
sed -i "1i $cmd" "$BOOTSTRAP_UNINSTALLER_CMDS"
}
C. Modifying Existing Helpers
Existing helpers automatically generate their own inverse commands.
pkg_install:add_rollback_cmd "pkg_remove $pkg"write_env_snippet/write_alias_snippet:add_rollback_cmd "rm -f \"$HOME/.config/bootstrap/env.d/$snippet_name.sh\""
D. New File Tracking Helpers
track_file() { add_rollback_cmd "sudo rm -f '$1'"; }
track_dir() { add_rollback_cmd "sudo rm -rf '$1'"; }
5. The Rollback Execution (b rollback <tool>)
Execution is line-by-line and fault-tolerant, allowing safe recovery even if a user injects a malformed command.
log_info "Rolling back..."
while IFS= read -r cmd; do
[ -z "$cmd" ] && continue
log_info "Executing: $cmd"
eval "$cmd" || log_warn "Failed to execute rollback step: $cmd"
done < "$BOOTSTRAP_UNINSTALLER_CMDS"
rm -f "$BOOTSTRAP_UNINSTALLER_CMDS"
log_success "Rollback complete."
6. Resilience Against User Modifications
Because b ware <tool> allows users to modify installation scripts:
- Dynamic Adaptation: The manifest is built during execution, adapting to whatever packages the user manually added.
- Fault Isolation: The
evalloop ensures that a syntax error in one custom rollback step doesn't crash the removal of other tracked packages.
7. Handling Shared Dependencies
The pkg_remove helper utilizes reference counting via simple text files (e.g., ~/.local/state/bootstrap/packages/curl).
- On
pkg_install: Append tool name. - On
pkg_remove: Remove tool name. If empty, proceed with system uninstallation.