mirror of
https://github.com/sortedcord/bootstrap.git
synced 2026-07-22 20:22:50 +05:30
docs: Update documentation to incorporate for Installation Strategies Redesign
This commit is contained in:
389
docs/installation_strategies.md
Normal file
389
docs/installation_strategies.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# Installation Strategies & Installer Lifecycle
|
||||
|
||||
This document explains how Bootstrap categorizes tool installations, the anatomy of an installer script, the full execution lifecycle from invocation to registry recording, and the helper infrastructure available to installers.
|
||||
|
||||
## 1. Installation Strategies
|
||||
|
||||
Every installer declares an explicit **strategy** that describes how it provisions software onto the system. The strategy is recorded in two places:
|
||||
|
||||
1. **At authoring time** — as a `# Strategy:` metadata comment in the installer script header.
|
||||
2. **At runtime** — inside `$BOOTSTRAP_STATE_DIR/registry.json` via `register_tool`.
|
||||
|
||||
### Strategy Types
|
||||
|
||||
| Strategy | Description | Validation (`registry_check`) |
|
||||
| :--- | :--- | :--- |
|
||||
| `binary` | Tool is downloaded as a prebuilt binary (typically from a GitHub release) and placed into `$BOOTSTRAP_BIN`. | Confirms the registered binary path exists and is executable. |
|
||||
| `managed` | Tool is installed through its own dedicated version manager or installer (e.g. NVM for Node.js, `rustup` for Rust). Bootstrap orchestrates the manager but doesn't own the binary directly. | Confirms the tool is available via `command -v`. |
|
||||
| `system` | Tool is installed entirely through the system package manager (`pacman`, `apt`, `dnf`). | Validates recorded system packages using `pkg_check`, falling back to `command -v` when no dependencies are recorded. |
|
||||
|
||||
### Which Strategy to Use
|
||||
|
||||
- **Prefer `binary`** when the upstream project provides standalone Linux binaries on GitHub (e.g. `bat`, `lazygit`, `hyperfine`, `yazi`, `nvim`).
|
||||
- **Use `managed`** when the tool has its own version/runtime manager that must be preserved for updates (e.g. `nvm` for Node.js, `rustup` for Rust, `zoxide` which uses its own install script).
|
||||
- **Use `system`** when the tool is only available through the distro's package manager and there is no upstream binary distribution (e.g. `docker`, `yay`).
|
||||
|
||||
## 2. Installer Script Anatomy
|
||||
|
||||
Every installer lives at `installers/install_<tool>.sh` and follows a strict structure.
|
||||
|
||||
### 2.1 Metadata Header
|
||||
|
||||
The first lines of the file are structured comments parsed by `scripts/generate_registry.sh` to auto-generate `lib/registry.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Tool: bat
|
||||
# DisplayName: Bat
|
||||
# Description: Install Bat (alternative to cat) and configure alias
|
||||
# Strategy: binary
|
||||
#
|
||||
```
|
||||
|
||||
| Field | Purpose |
|
||||
| :--- | :--- |
|
||||
| `Tool` | The CLI key used to invoke the installer (`b bat`). Must be lowercase, no spaces. |
|
||||
| `DisplayName` | Human-readable name shown in `b ware` listings and log messages. |
|
||||
| `Description` | One-line description. The word "Install" at the start is stripped automatically by the registry generator. |
|
||||
| `Strategy` | One of `binary`, `managed`, or `system`. |
|
||||
|
||||
### 2.2 Script Body Structure
|
||||
|
||||
```bash
|
||||
set -euo pipefail
|
||||
|
||||
# Temporary directory (auto-cleaned)
|
||||
TMP_DIR="$(make_temp_dir)"
|
||||
cleanup() { rm -rf "$TMP_DIR"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
# Core installation function
|
||||
install_<tool>() {
|
||||
# 1. Check if already installed, offer reinstall/upgrade
|
||||
# 2. Detect architecture, fetch latest version
|
||||
# 3. Download, extract, install
|
||||
# 4. Track files for rollback
|
||||
# 5. Register in the JSON registry
|
||||
}
|
||||
|
||||
# Optional: shell environment configuration
|
||||
configure_shell() {
|
||||
# Write env/alias/completion snippets
|
||||
}
|
||||
|
||||
main() {
|
||||
install_<tool>
|
||||
configure_shell # if needed
|
||||
|
||||
echo
|
||||
log_success "<Tool> installation and configuration complete."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
### 2.3 Key Conventions
|
||||
|
||||
- **No standalone execution guards**: Installers are always executed by the Bootstrap runtime, which sources all libraries before running the script. They must never check for or source libraries themselves.
|
||||
- **No `curl` availability checks**: The runtime guarantees `curl` is available.
|
||||
- **No direct `.bashrc` manipulation**: Use `write_env_snippet`, `write_alias_snippet`, and `write_completion_snippet` to write shell configuration fragments into the snippet directories (`env.d/`, `aliases.d/`, `completions.d/`). The loader sources these automatically.
|
||||
- **Temp directories**: Use `make_temp_dir` (from `lib/common.sh`) for temporary work and clean up via a `trap cleanup EXIT`.
|
||||
|
||||
## 3. Available Helper Functions
|
||||
|
||||
Installers run inside a prepared environment where all Bootstrap libraries have been sourced. The following helper functions are available:
|
||||
|
||||
### 3.1 Common Utilities (`lib/common.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `log_info`, `log_success`, `log_warn`, `log_error` | Coloured logging to stdout/stderr. |
|
||||
| `confirm "<prompt>"` | Interactive yes/no prompt. Reads from `/dev/tty` to support piped execution. |
|
||||
| `has_command <cmd>` | Returns `0` if the command exists on `$PATH`. |
|
||||
| `make_temp_dir` | Creates a `mktemp -d` temporary directory and prints its path. |
|
||||
| `download_file <url> <dest>` | Cached, resumable download. Files are first stored in `$BOOTSTRAP_CACHE_DIR/downloads/` and then copied to `<dest>`. |
|
||||
| `version_lt <v1> <v2>` | Semver comparison. Returns `0` if `v1 < v2`. |
|
||||
|
||||
### 3.2 Platform & Package Management (`lib/platform.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `detect_distro` | Returns `arch`, `debian`, `fedora`, or `unknown`. |
|
||||
| `detect_arch` | Returns `x86_64` or `arm64`. |
|
||||
| `pkg_install <spec>...` | Installs system packages. Supports distro-specific mappings: `"arch:docker\|debian:docker.io\|fedora:docker"`. |
|
||||
| `pkg_check <spec>...` | Returns `0` if all specified packages are installed. |
|
||||
| `pkg_remove <spec>...` | Removes system packages. |
|
||||
|
||||
### 3.3 GitHub Release Helpers (`lib/github.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `github_get_latest_release <owner/repo>` | Queries the GitHub API and prints the `tag_name` of the latest release. |
|
||||
| `github_get_download_url <owner/repo> <tag> <regex>` | Finds the asset matching the regex pattern in the given release and prints its download URL. |
|
||||
| `github_download_asset <owner/repo> <tag> <regex> <dest>` | Resolves and downloads the matching asset to `<dest>`. |
|
||||
|
||||
**Why `github_get_latest_release` is separate from `github_download_asset`**: Asset filenames often embed the version string (e.g. `bat-v0.26.1-x86_64-unknown-linux-gnu.tar.gz`). The installer needs the concrete version *before* constructing the asset pattern and also passes it to `register_tool`.
|
||||
|
||||
### 3.4 Shell Configuration (`lib/shell_config.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `write_env_snippet <name> <content>` | Writes content to `$BOOTSTRAP_DIR/env.d/<name>.sh`. Auto-registers a rollback command to delete the snippet. |
|
||||
| `write_alias_snippet <name> <content>` | Writes content to `$BOOTSTRAP_DIR/aliases.d/<name>.sh`. Auto-registers rollback. |
|
||||
| `write_completion_snippet <name> <content>` | Writes content to `$BOOTSTRAP_DIR/completions.d/<name>.sh`. Auto-registers rollback. |
|
||||
|
||||
### 3.5 Rollback & File Tracking (`lib/rollback.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `track_file <path>` | Records `sudo rm -f '<path>'` in the uninstaller manifest for the current tool. |
|
||||
| `track_dir <path>` | Records `sudo rm -rf '<path>'` in the uninstaller manifest. |
|
||||
| `add_rollback_cmd <cmd>` | Prepends an arbitrary shell command to the manifest (LIFO order). |
|
||||
|
||||
### 3.6 Registry Helpers (`lib/registry_helpers.sh`)
|
||||
|
||||
| Function | Purpose |
|
||||
| :--- | :--- |
|
||||
| `register_tool <tool> <strategy> [version] [source]` | Records the tool in `registry.json` with its strategy, version, source, timestamp, and (for `binary` strategy) its binary path. |
|
||||
| `registry_add_sys_deps <tool> <dep1> <dep2>...` | Appends system package dependency names to the tool's `system_dependencies` array in the registry (used for reference counting during uninstallation). |
|
||||
| `registry_remove_tool <tool>` | Deletes the tool's entire entry from `registry.json`. |
|
||||
| `registry_check <tool>` | Validates that the tool is correctly installed according to its declared strategy. |
|
||||
|
||||
## 4. Execution Lifecycle
|
||||
|
||||
This is the complete flow from when a user types `b <tool>` to when the installation is recorded.
|
||||
|
||||
### 4.1 Invocation
|
||||
|
||||
```
|
||||
User types: b bat
|
||||
│
|
||||
▼
|
||||
b.sh (shell function)
|
||||
│
|
||||
├── Auto-update check (once per 24h)
|
||||
│
|
||||
▼
|
||||
bash routes.sh bat
|
||||
```
|
||||
|
||||
`b.sh` is a shell function sourced from `~/.bashrc`. It delegates to `routes.sh` in a subshell.
|
||||
|
||||
### 4.2 Library Loading
|
||||
|
||||
`routes.sh` sources the core libraries in order:
|
||||
|
||||
1. `lib/common.sh` — Logging, `download_file`, `make_temp_dir`, XDG path variables
|
||||
2. `lib/rollback.sh` — Manifest system, `track_file`, `track_dir`, `init_rollback_system`
|
||||
3. `lib/platform.sh` — Distro/arch detection, `pkg_install`/`pkg_remove`
|
||||
4. `lib/shell_config.sh` — `write_env_snippet`, `write_alias_snippet`, etc.
|
||||
5. `lib/registry.sh` — Auto-generated lookup tables (`INSTALLERS`, `INSTALLER_DISPLAYS`, `INSTALLER_STRATEGIES`, `INSTALLER_KEYS`)
|
||||
6. `lib/registry_helpers.sh` — `register_tool`, `registry_add_sys_deps`, etc.
|
||||
7. `lib/github.sh` — GitHub release API helpers
|
||||
8. `lib/plugins.sh` — Plugin system
|
||||
|
||||
All functions are exported to subshells so the installer script inherits them.
|
||||
|
||||
### 4.3 Routing
|
||||
|
||||
`routes.sh` resolves the command. If the argument matches a key in the `INSTALLERS` registry, it is treated as a tool name and routed to `run_ware`:
|
||||
|
||||
```
|
||||
routes.sh receives "bat"
|
||||
│
|
||||
├── Is it a built-in command (help, rb, fall, con, up, gone, etc.)? → No
|
||||
├── Is it a registered tool key in INSTALLERS? → Yes
|
||||
│
|
||||
▼
|
||||
run_ware "bat" "-y"
|
||||
```
|
||||
|
||||
### 4.4 Installer Resolution
|
||||
|
||||
`run_ware` locates the installer script:
|
||||
|
||||
1. **Local check**: Does `$BOOTSTRAP_DIR/installers/install_bat.sh` exist? If yes, use it directly.
|
||||
2. **Remote download**: If not local, download from the primary Git server (`git.adityagupta.dev`). On failure, fall back to the GitHub mirror.
|
||||
|
||||
### 4.5 Pre-Execution Setup
|
||||
|
||||
Before running the installer, `run_ware` calls `setup_uninstaller_context "bat"`, which:
|
||||
|
||||
- Sets `BOOTSTRAP_CURRENT_TOOL=bat`
|
||||
- Sets `BOOTSTRAP_UNINSTALLER_CMDS=$BOOTSTRAP_STATE_DIR/uninstallers/bat.cmds`
|
||||
- If a manifest already exists from a previous interrupted run, it preserves it (resume support).
|
||||
- Otherwise, creates a fresh empty manifest file.
|
||||
|
||||
Signal traps (`INT`, `TERM`) are installed to catch interruptions.
|
||||
|
||||
### 4.6 Installer Execution
|
||||
|
||||
The installer script runs in a `bash` subshell. During execution:
|
||||
|
||||
- **`track_file`/`track_dir`** calls prepend removal commands to `bat.cmds` (LIFO order).
|
||||
- **`write_env_snippet`/`write_alias_snippet`** automatically record their own rollback commands.
|
||||
- **`pkg_install`** installs system packages. If the tool uses `system` strategy, the installer calls `registry_add_sys_deps` to register these packages for reference counting.
|
||||
- **`register_tool "bat" "binary" "$version" "github:sharkdp/bat"`** writes the tool's metadata to `$BOOTSTRAP_STATE_DIR/registry.json` using thread-safe `flock`.
|
||||
|
||||
### 4.7 Post-Execution
|
||||
|
||||
If the installer exits successfully (`exit 0`), `run_ware`:
|
||||
|
||||
1. Calls `mark_install_success "bat"` — appends `INSTALL: bat` to `$BOOTSTRAP_STATE_DIR/history.log`.
|
||||
2. Re-sources `~/.bashrc` so new env/alias snippets take effect immediately.
|
||||
|
||||
If the installer fails or is interrupted, the user is offered a choice:
|
||||
|
||||
- **Rollback (`r`)**: Executes the manifest line-by-line to undo all recorded changes.
|
||||
- **Keep (`k`)**: Preserves partial changes so the user can resume or debug.
|
||||
|
||||
## 5. The Registry (`registry.json`)
|
||||
|
||||
The JSON registry is the central source of truth for installed tool state. It lives at `$BOOTSTRAP_STATE_DIR/registry.json`.
|
||||
|
||||
### 5.1 Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"bat": {
|
||||
"strategy": "binary",
|
||||
"version": "v0.26.1",
|
||||
"source": "github:sharkdp/bat",
|
||||
"installed_at": "2025-06-28T03:15:00Z",
|
||||
"bin": "/home/user/.local/share/bootstrap/bin/bat"
|
||||
},
|
||||
"docker": {
|
||||
"strategy": "system",
|
||||
"installed_at": "2025-06-28T03:20:00Z",
|
||||
"source": "os-package-manager",
|
||||
"system_dependencies": [
|
||||
"arch:docker|debian:docker.io|fedora:docker"
|
||||
]
|
||||
},
|
||||
"node": {
|
||||
"strategy": "managed",
|
||||
"version": "v0.40.5",
|
||||
"source": "github:nvm-sh/nvm",
|
||||
"installed_at": "2025-06-28T03:25:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Thread Safety
|
||||
|
||||
All mutations go through `registry_set`, which acquires an exclusive file lock (`flock -x`) on `registry.json.lock` before applying a `jq` filter. This prevents race conditions if multiple installers run simultaneously.
|
||||
|
||||
## 6. The Auto-Generated Registry (`lib/registry.sh`)
|
||||
|
||||
`lib/registry.sh` is a **build-time artifact**, not a runtime state file. It is auto-generated by `scripts/generate_registry.sh`, which parses the metadata headers from every `installers/install_*.sh` file and produces four Bash data structures:
|
||||
|
||||
| Variable | Type | Purpose |
|
||||
| :--- | :--- | :--- |
|
||||
| `INSTALLERS` | `declare -A` | Maps tool key → description |
|
||||
| `INSTALLER_DISPLAYS` | `declare -A` | Maps tool key → human-readable display name |
|
||||
| `INSTALLER_STRATEGIES` | `declare -A` | Maps tool key → strategy (`binary`/`managed`/`system`) |
|
||||
| `INSTALLER_KEYS` | Array | Sorted list of all tool keys |
|
||||
|
||||
These are used by `routes.sh` for command routing, by `b ware` (with no arguments) for listing available tools, and by `b help` for the help output.
|
||||
|
||||
**Important**: `lib/registry.sh` must be regenerated whenever an installer is added, removed, or has its metadata header changed. Run:
|
||||
|
||||
```bash
|
||||
./scripts/generate_registry.sh
|
||||
```
|
||||
|
||||
## 7. Uninstallation Flow
|
||||
|
||||
### 7.1 Named Uninstall (`b rb <tool>`)
|
||||
|
||||
```
|
||||
b rb bat
|
||||
│
|
||||
▼
|
||||
uninstall_tool "bat"
|
||||
│
|
||||
├── 1. execute_rollback "bat"
|
||||
│ → Reads bat.cmds line-by-line (LIFO)
|
||||
│ → Executes: rm binary, rm env snippet, rm alias snippet, etc.
|
||||
│ → Deletes bat.cmds
|
||||
│
|
||||
├── 2. Reference-count system dependencies
|
||||
│ → For each dep in registry_get_sys_deps("bat"):
|
||||
│ → Count how many OTHER tools also list this dep
|
||||
│ → If count == 0: pkg_remove the dep
|
||||
│ → If count > 0: keep it
|
||||
│
|
||||
├── 3. registry_remove_tool "bat"
|
||||
│ → Deletes .tools.bat from registry.json
|
||||
│
|
||||
└── 4. Remove "INSTALL: bat" from history.log
|
||||
```
|
||||
|
||||
### 7.2 Multi-Tool Uninstall (`b rb bat,yazi,zoxide`)
|
||||
|
||||
The argument is split on commas and `uninstall_tool` is called for each tool sequentially.
|
||||
|
||||
### 7.3 Bare Rollback (`b rb`)
|
||||
|
||||
Reads the last line of `history.log`. If it is `INSTALL: <tool>`, calls `uninstall_tool` for that tool.
|
||||
|
||||
### 7.4 Savepoint Rollback (`b rb <savepoint>`)
|
||||
|
||||
If the argument doesn't match a registered tool, it is treated as a savepoint name. The system walks `history.log` backwards, uninstalling each tool encountered until it reaches the matching `SAVEPOINT:` entry.
|
||||
|
||||
## 8. Strategy-Specific Patterns
|
||||
|
||||
### 8.1 Binary Strategy Example (Bat)
|
||||
|
||||
```bash
|
||||
# Detect arch, fetch latest tag from GitHub
|
||||
latest_tag=$(github_get_latest_release "sharkdp/bat")
|
||||
|
||||
# Download the matching release asset
|
||||
github_download_asset "sharkdp/bat" "$latest_tag" "bat-${latest_tag}-${target}\.tar\.gz" "$archive"
|
||||
|
||||
# Extract, copy binary to $BOOTSTRAP_BIN, make executable
|
||||
cp "$extract_dir/bat" "$BOOTSTRAP_BIN/bat"
|
||||
chmod +x "$BOOTSTRAP_BIN/bat"
|
||||
track_file "$BOOTSTRAP_BIN/bat"
|
||||
|
||||
# Register
|
||||
register_tool "bat" "binary" "$latest_tag" "github:sharkdp/bat"
|
||||
```
|
||||
|
||||
### 8.2 Managed Strategy Example (Node.js via NVM)
|
||||
|
||||
```bash
|
||||
# Download and extract NVM into $BOOTSTRAP_RUNTIMES/nvm
|
||||
download_file "$nvm_url" "$TMP_DIR/nvm.tar.gz"
|
||||
tar -xzf "$TMP_DIR/nvm.tar.gz" -C "$BOOTSTRAP_RUNTIMES/nvm" --strip-components=1
|
||||
track_dir "$BOOTSTRAP_RUNTIMES/nvm"
|
||||
|
||||
# Write env snippet so NVM loads on shell startup
|
||||
write_env_snippet "node" "$content"
|
||||
|
||||
# Use NVM to install Node LTS
|
||||
nvm install --lts
|
||||
|
||||
# Register (NVM manages the actual node binary)
|
||||
register_tool "node" "managed" "$latest_tag" "github:nvm-sh/nvm"
|
||||
```
|
||||
|
||||
### 8.3 System Strategy Example (Docker)
|
||||
|
||||
```bash
|
||||
# Install via system package manager
|
||||
pkg_install "arch:docker|debian:docker.io|fedora:docker"
|
||||
|
||||
# Register the dependency for reference counting
|
||||
registry_add_sys_deps "docker" "arch:docker|debian:docker.io|fedora:docker"
|
||||
|
||||
# Additional system configuration (groups, systemd)
|
||||
sudo usermod -aG docker "$USER"
|
||||
add_rollback_cmd "sudo gpasswd -d $USER docker || true"
|
||||
|
||||
# Register
|
||||
register_tool "docker" "system" "" "os-package-manager"
|
||||
```
|
||||
@@ -3,8 +3,10 @@
|
||||
## 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.
|
||||
## 2. Core Concept: Procedural Manifests & JSON Registry
|
||||
The system uses a hybrid approach:
|
||||
1. **Procedural Command Manifests**: As the installation progresses, a LIFO script manifest (`$BOOTSTRAP_STATE_DIR/uninstallers/<tool>.cmds`) is built by prepending the inverse commands for each helper action (files, directories, env/alias snippets).
|
||||
2. **Centralized JSON Registry**: Metadata, strategy, and system-level dependencies are tracked in a thread-safe `$BOOTSTRAP_STATE_DIR/registry.json` using `jq`. During uninstallation, this registry is used to reference-count and safely remove shared system dependencies.
|
||||
|
||||
## 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.
|
||||
@@ -33,17 +35,19 @@ INSTALL: nvim
|
||||
```
|
||||
|
||||
### C. Bare Rollback (`b rb`)
|
||||
When `b rb` is executed without arguments, it rolls back the single most recent change:
|
||||
When `b rb` is executed without arguments, it rolls back the single most recent installation:
|
||||
1. Reads the last line of the history log (e.g., `INSTALL: nvim`).
|
||||
2. Executes the command manifest for `nvim`.
|
||||
3. Deletes the last line from the history log.
|
||||
2. Runs `uninstall_tool nvim` which executes `nvim.cmds` and cleans up registry entries.
|
||||
|
||||
### D. Savepoint Rollback (`b rb <name>`)
|
||||
When `b rb init` is executed, it rolls back all changes made after that savepoint:
|
||||
### D. Named Rollback (`b rb <tool1>,<tool2>...`)
|
||||
Users can uninstall specific tools by name (e.g., `b rb nvim` or `b rb nvim,yazi`). The system runs the corresponding uninstaller manifests, reference-counts and removes any orphaned system dependencies, and cleans up the history log and registry.
|
||||
|
||||
### E. Savepoint Rollback (`b rb <savepoint>`)
|
||||
If the argument does not match an installed tool in the registry, it is treated as a savepoint:
|
||||
1. Parses the history log from bottom to top.
|
||||
2. For each `INSTALL: <tool>` encountered, it executes the rollback manifest for `<tool>`.
|
||||
3. Stops when it reaches `SAVEPOINT: init`.
|
||||
4. Truncates the history log back to the savepoint.
|
||||
2. For each `INSTALL: <tool>` encountered, it runs `uninstall_tool <tool>`.
|
||||
3. Stops when it reaches the specified `SAVEPOINT: <name>`.
|
||||
4. Truncates the history log back to that savepoint.
|
||||
|
||||
## 4. Required Abstractions & Helper Modifications
|
||||
|
||||
@@ -64,22 +68,18 @@ add_rollback_cmd() {
|
||||
}
|
||||
```
|
||||
|
||||
### C. Modifying Existing Helpers
|
||||
Existing helpers automatically generate their own inverse commands.
|
||||
- **`pkg_install`:**
|
||||
### C. Helper Operations
|
||||
Helper functions record their inverse actions directly to the manifest:
|
||||
- **`write_env_snippet` / `write_alias_snippet` / `write_completion_snippet`:**
|
||||
```bash
|
||||
add_rollback_cmd "pkg_remove $pkg"
|
||||
add_rollback_cmd "rm -f \"\$BOOTSTRAP_DIR/env.d/\$snippet_name.sh\""
|
||||
```
|
||||
- **`write_env_snippet` / `write_alias_snippet`:**
|
||||
- **`track_file` / `track_dir`**:
|
||||
```bash
|
||||
add_rollback_cmd "rm -f \"$HOME/.config/bootstrap/env.d/$snippet_name.sh\""
|
||||
track_file() { add_rollback_cmd "sudo rm -f '$1'"; }
|
||||
track_dir() { add_rollback_cmd "sudo rm -rf '$1'"; }
|
||||
```
|
||||
|
||||
### D. New File Tracking Helpers
|
||||
```bash
|
||||
track_file() { add_rollback_cmd "sudo rm -f '$1'"; }
|
||||
track_dir() { add_rollback_cmd "sudo rm -rf '$1'"; }
|
||||
```
|
||||
Note: distro packages are not tracked via `add_rollback_cmd`. Instead, installers declare them as system dependencies (see below).
|
||||
|
||||
## 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.
|
||||
@@ -102,9 +102,11 @@ Because `b ware <tool>` allows users to modify installation scripts:
|
||||
2. **Fault Isolation:** The `eval` loop 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.
|
||||
System dependencies installed via `pkg_install` must be registered via `registry_add_sys_deps <tool> <dep1> <dep2>...`.
|
||||
When a tool is uninstalled:
|
||||
1. The registry is checked to see if any other installed tool still lists those dependencies.
|
||||
2. If the reference count drops to `0`, the dependency is automatically removed using `pkg_remove`.
|
||||
3. If other tools still depend on it, it is kept.
|
||||
|
||||
## 8. Fault Tolerance, Resumability, and Interrupted Installations
|
||||
|
||||
|
||||
26
readme.md
26
readme.md
@@ -79,7 +79,7 @@ It automatically fuzzy-finds the folder in case there is no exact match. Also, i
|
||||
|
||||
### Rollbacks and Savepoints (`b rb` and `b fall`)
|
||||
|
||||
Bootstrap CLI features a powerful, procedural rollback system. It strictly tracks every extracted binary, configuration snippet, and package manager transaction to ensure your environment stays clean.
|
||||
Bootstrap CLI features a robust rollback and uninstallation system driven by procedural manifests and a centralized JSON registry.
|
||||
|
||||
To safely uninstall the very last tool you installed (including wiping its shell paths and aliases):
|
||||
|
||||
@@ -87,11 +87,21 @@ To safely uninstall the very last tool you installed (including wiping its shell
|
||||
b rb
|
||||
```
|
||||
|
||||
To uninstall specific tools by name (supporting single or comma-separated lists of tools):
|
||||
|
||||
```bash
|
||||
b rb nvim
|
||||
b rb bat,yazi,zoxide
|
||||
```
|
||||
|
||||
Shared system package dependencies are reference-counted in the registry and will be automatically cleaned up only when the last tool depending on them is removed.
|
||||
|
||||
To create a named savepoint before experimenting with your setup:
|
||||
|
||||
```bash
|
||||
b fall pre_dev_setup
|
||||
```
|
||||
*(Note: Savepoint names cannot conflict with the names of any available tools).*
|
||||
|
||||
To completely roll back all installations made after that savepoint, restoring your system back to that exact state:
|
||||
|
||||
@@ -177,6 +187,20 @@ b gone -f
|
||||
|
||||
Then reload your shell configuration or run `unset -f b` to clear the function definition from your current terminal session.
|
||||
|
||||
## Directory Layout
|
||||
|
||||
Bootstrap isolates all of its components and installed software inside XDG-compliant directories. You can override these variables in your shell environment, but they default to the following:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| :--- | :--- | :--- |
|
||||
| `BOOTSTRAP_DIR` | `~/.config/bootstrap` | Core configuration, libraries, shell snippets (`env.d`, `aliases.d`, `completions.d`), and local installers |
|
||||
| `BOOTSTRAP_DATA_DIR` | `~/.local/share/bootstrap` | Active installation bin files, optional components, and runtime installations |
|
||||
| `BOOTSTRAP_STATE_DIR` | `~/.local/state/bootstrap` | Stateful registry, logs, and rollback/uninstall manifests |
|
||||
| `BOOTSTRAP_CACHE_DIR` | `~/.cache/bootstrap` | Downloaded files cache and temporary workspace files |
|
||||
| `BOOTSTRAP_BIN` | `$BOOTSTRAP_DATA_DIR/bin` | Target directory for all installed tools' binaries |
|
||||
| `BOOTSTRAP_OPT` | `$BOOTSTRAP_DATA_DIR/opt` | Destination for complex, multi-file software distributions |
|
||||
| `BOOTSTRAP_RUNTIMES` | `$BOOTSTRAP_DATA_DIR/runtimes` | Workspace for programming language runtime managers (e.g. NVM) |
|
||||
|
||||
## Development
|
||||
|
||||
If you are developing this tool locally:
|
||||
|
||||
Reference in New Issue
Block a user