feat: Dynamic package registry

This commit is contained in:
2026-06-20 19:05:20 +05:30
parent bad324a5cd
commit e658d6d375
20 changed files with 210 additions and 53 deletions

View File

@@ -35,22 +35,16 @@ When adding a new installer named `<name>`:
Create `installers/install_<name>.sh` using the template below.
### Step 2: Register in `routes.sh`
### Step 2: Add metadata comments to the top of your installer script
Make **two** edits to `routes.sh`:
At the top of your new installer script, right below `#!/usr/bin/env bash`, add the following three metadata headers:
```bash
# Tool: <name>
# DisplayName: <displayName>
# Description: <description>
```
1. **Add to the `INSTALLERS` associative array** (line ~19-26). Insert a new entry in alphabetical order:
```bash
[<name>]="Short description of what it installs"
```
2. **Add to the `INSTALLER_KEYS` array** (line ~28). Insert the key in alphabetical order:
```bash
INSTALLER_KEYS=(agy bat <name> node nvim yazi zoxide)
```
> [!IMPORTANT]
> Both arrays must be kept in sync and in alphabetical order.
The central router `routes.sh` and autocomplete function in `b.sh` will dynamically parse this metadata from all `install_*.sh` scripts to register the installer and keys automatically! No manual edits to `routes.sh` or `b.sh` are required.
### Step 3: Verify (optional)
@@ -67,6 +61,9 @@ Every installer follows this exact boilerplate structure. Copy this and fill in
```bash
#!/usr/bin/env bash
# Tool: <name>
# DisplayName: <ToolName>
# Description: Short description of what it installs
#
# <ToolName> Installer Script
#

View File

@@ -1 +1 @@
1.1.1
1.1.2

43
b.sh
View File

@@ -77,16 +77,23 @@ _b_completion() {
# If completing the first argument after 'b'
if [ "$COMP_CWORD" -eq 1 ]; then
opts="all con bye up ware"
opts="all con bye up ware bware"
local routes_file="$HOME/.config/bootstrap/routes.sh"
if [ -f "$routes_file" ]; then
# Extract installer keys dynamically from the routes.sh file
local installer_keys
installer_keys=$(grep -E "^INSTALLER_KEYS=" "$routes_file" 2>/dev/null | sed -E 's/INSTALLER_KEYS=\(([^)]+)\)/\1/' 2>/dev/null)
if [ -n "$installer_keys" ]; then
opts="$opts $installer_keys"
fi
local routes_dir="$HOME/.config/bootstrap"
local installer_keys=""
if [ -d "$routes_dir/installers" ]; then
for f in "$routes_dir/installers"/install_*.sh; do
if [ -f "$f" ]; then
local tool
tool=$(grep -E "^# Tool:" "$f" | head -n1 | sed -E 's/^# Tool:\s*//I')
if [ -n "$tool" ]; then
installer_keys="$installer_keys $tool"
fi
fi
done
fi
if [ -n "$installer_keys" ]; then
opts="$opts $installer_keys"
fi
# Support comma-separated completions (e.g. b nvim,ya<TAB>)
@@ -111,12 +118,20 @@ _b_completion() {
return 0
fi
# If completing arguments for 'b ware <tool>'
if [ "$COMP_CWORD" -eq 2 ] && [ "$prev" = "ware" ]; then
local routes_file="$HOME/.config/bootstrap/routes.sh"
# If completing arguments for 'b ware <tool>' or 'b bware <tool>'
if [ "$COMP_CWORD" -eq 2 ] && { [ "$prev" = "ware" ] || [ "$prev" = "bware" ]; }; then
local routes_dir="$HOME/.config/bootstrap"
local installer_keys=""
if [ -f "$routes_file" ]; then
installer_keys=$(grep -E "^INSTALLER_KEYS=" "$routes_file" 2>/dev/null | sed -E 's/INSTALLER_KEYS=\(([^)]+)\)/\1/' 2>/dev/null)
if [ -d "$routes_dir/installers" ]; then
for f in "$routes_dir/installers"/install_*.sh; do
if [ -f "$f" ]; then
local tool
tool=$(grep -E "^# Tool:" "$f" | head -n1 | sed -E 's/^# Tool:\s*//I')
if [ -n "$tool" ]; then
installer_keys="$installer_keys $tool"
fi
fi
done
fi
[ -z "$installer_keys" ] && installer_keys="agy bat node nvim pnpm rust starship yay yazi zoxide"

View File

@@ -90,6 +90,7 @@ install_bootstrap() {
"VERSION"
"b.sh"
"routes.sh"
"registry.sh"
"lib/common.sh"
"lib/platform.sh"
"lib/shell_config.sh"

View File

@@ -9,7 +9,4 @@ printf " %-6s - %s\n" "up" "Check for updates and update Bootstrap CLI"
printf " %-6s - %s\n" "ware" "Edit and run an installer (e.g. b ware nvim)"
printf " %-6s - %s\n" "bye" "Uninstall Bootstrap CLI helper"
# Installers second
for key in "${INSTALLER_KEYS[@]}"; do
printf " %-6s - %s\n" "$key" "${INSTALLERS[$key]}"
done

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: agy
# DisplayName: Antigravity
# Description: Install Antigravity CLI
#
# Antigravity CLI Installer Script (Linux Only)
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: bat
# DisplayName: Bat
# Description: Install Bat (alternative to cat) and configure alias
#
# Bat Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: node
# DisplayName: Node
# Description: Install Node.js (LTS) and NVM
#
# Node.js and NVM Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: nvim
# DisplayName: Neovim
# Description: Install Neovim 0.11.7 and configuration
#
# Neovim Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: pnpm
# DisplayName: Pnpm
# Description: Install pnpm package manager
#
# pnpm Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: rust
# DisplayName: Rust
# Description: Install Rustup and Rust compiler/toolchain
#
# Rust Installer Script (Simplified Local Rustup Init)
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: starship
# DisplayName: Starship
# Description: Install Starship shell prompt
#
# Starship Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: yay
# DisplayName: Yay
# Description: Install Yay AUR helper
#
# Yay Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: yazi
# DisplayName: Yazi
# Description: Install Yazi terminal file manager and dependencies
#
# Yazi Installer Script
#

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Tool: zoxide
# DisplayName: Zoxide
# Description: Install Zoxide directory jumper
#
# Zoxide Installer Script
#

View File

@@ -58,6 +58,12 @@ To bypass the editor and install the tool directly using the `ware` command, app
b ware nvim -y
```
To list all available installer tools and their descriptions, run the `ware` (or `bware`) command without any arguments:
```bash
b ware
```
You can also edit configurations located in your `~/.config/` directory by running:
```bash

29
registry.sh Normal file
View File

@@ -0,0 +1,29 @@
# This file is auto-generated by scripts/generate_registry.sh. Do not edit manually.
declare -A INSTALLERS=(
[agy]="Install Antigravity CLI"
[bat]="Install Bat (alternative to cat) and configure alias"
[node]="Install Node.js (LTS) and NVM"
[nvim]="Install Neovim 0.11.7 and configuration"
[pnpm]="Install pnpm package manager"
[rust]="Install Rustup and Rust compiler/toolchain"
[starship]="Install Starship shell prompt"
[yay]="Install Yay AUR helper"
[yazi]="Install Yazi terminal file manager and dependencies"
[zoxide]="Install Zoxide directory jumper"
)
declare -A INSTALLER_DISPLAYS=(
[agy]="Antigravity"
[bat]="Bat"
[node]="Node"
[nvim]="Neovim"
[pnpm]="Pnpm"
[rust]="Rust"
[starship]="Starship"
[yay]="Yay"
[yazi]="Yazi"
[zoxide]="Zoxide"
)
INSTALLER_KEYS=(agy bat node nvim pnpm rust starship yay yazi zoxide)

View File

@@ -15,21 +15,35 @@ fi
require_bash
# Registry of installers
declare -A INSTALLERS=(
[agy]="Install Antigravity CLI"
[bat]="Install Bat (alternative to cat) and configure alias"
[node]="Install Node.js (LTS) and NVM"
[nvim]="Install Neovim 0.11.7 and configuration"
[pnpm]="Install pnpm package manager"
[rust]="Install Rustup and Rust compiler/toolchain"
[starship]="Install Starship shell prompt"
[yay]="Install Yay AUR helper"
[yazi]="Install Yazi terminal file manager and dependencies"
[zoxide]="Install Zoxide directory jumper"
)
# Order in which installers should be displayed
INSTALLER_KEYS=(agy bat node nvim pnpm rust starship yay yazi zoxide)
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null || pwd)"
# Source registry
if [ -f "$_SCRIPT_DIR/registry.sh" ]; then
. "$_SCRIPT_DIR/registry.sh"
elif [ -f "$BOOTSTRAP_DIR/registry.sh" ]; then
. "$BOOTSTRAP_DIR/registry.sh"
else
# Standalone/remote fallback: download registry
_tmp_registry=$(mktemp)
BOOTSTRAP_BASE_URL="${BOOTSTRAP_BASE_URL:-https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master}"
BOOTSTRAP_FALLBACK_URL="${BOOTSTRAP_FALLBACK_URL:-https://raw.githubusercontent.com/sortedcord/bootstrap/refs/heads/master}"
if has_command curl; then
curl -fsSL "${BOOTSTRAP_BASE_URL}/registry.sh" -o "$_tmp_registry" 2>/dev/null || \
curl -fsSL "${BOOTSTRAP_FALLBACK_URL}/registry.sh" -o "$_tmp_registry" 2>/dev/null
elif has_command wget; then
wget -qO "$_tmp_registry" "${BOOTSTRAP_BASE_URL}/registry.sh" 2>/dev/null || \
wget -qO "$_tmp_registry" "${BOOTSTRAP_FALLBACK_URL}/registry.sh" 2>/dev/null
fi
if [ -s "$_tmp_registry" ]; then
. "$_tmp_registry"
else
# Critical fallback
declare -A INSTALLERS
declare -A INSTALLER_DISPLAYS
INSTALLER_KEYS=()
fi
rm -f "$_tmp_registry"
fi
# Helper function to run/edit installer scripts
run_ware() {
@@ -47,8 +61,11 @@ run_ware() {
fi
done
# Capitalize first letter for display (e.g. nvim -> Neovim)
local display_name="$(echo "${tool:0:1}" | tr '[:lower:]' '[:upper:]')${tool:1}"
# Resolve display name from metadata or fallback
local display_name="${INSTALLER_DISPLAYS[$tool]:-}"
if [ -z "$display_name" ]; then
display_name="$(echo "${tool:0:1}" | tr '[:lower:]' '[:upper:]')${tool:1}"
fi
# Check for local installer first
local local_installer="$BOOTSTRAP_DIR/installers/install_${tool}.sh"
@@ -166,11 +183,14 @@ for script in "${SCRIPTS[@]}"; do
exit 1
fi
;;
ware)
ware|bware)
tools_arg="${1:-}"
if [ -z "$tools_arg" ]; then
log_error "Usage: b ware <tool1,tool2,...> [-y]"
exit 1
echo "Available tools:"
for key in "${INSTALLER_KEYS[@]}"; do
printf " %-10s - %s\n" "$key" "${INSTALLERS[$key]}"
done
exit 0
fi
shift
IFS=',' read -ra WARE_TOOLS <<< "$tools_arg"

56
scripts/generate_registry.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Automatically generate registry.sh from installer headers.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
INSTALLERS_DIR="$REPO_DIR/installers"
REGISTRY_FILE="$REPO_DIR/registry.sh"
echo "==> Generating registry.sh..."
# Temporary arrays
declare -A tools_desc
declare -A tools_disp
keys=()
for f in "$INSTALLERS_DIR"/install_*.sh; do
[ -f "$f" ] || continue
tool=$(grep -E "^# Tool:" "$f" | head -n1 | sed -E 's/^# Tool:\s*//I')
disp_name=$(grep -E "^# DisplayName:" "$f" | head -n1 | sed -E 's/^# DisplayName:\s*//I')
desc=$(grep -E "^# Description:" "$f" | head -n1 | sed -E 's/^# Description:\s*//I')
if [ -n "$tool" ]; then
tools_desc["$tool"]="$desc"
tools_disp["$tool"]="${disp_name:-$tool}"
keys+=("$tool")
fi
done
# Sort keys alphabetically
sorted_keys=($(printf '%s\n' "${keys[@]}" | sort))
{
echo "# This file is auto-generated by scripts/generate_registry.sh. Do not edit manually."
echo ""
echo "declare -A INSTALLERS=("
for k in "${sorted_keys[@]}"; do
# Escape any double quotes in description
escaped_desc=$(echo "${tools_desc[$k]}" | sed 's/"/\\"/g')
echo " [$k]=\"$escaped_desc\""
done
echo ")"
echo ""
echo "declare -A INSTALLER_DISPLAYS=("
for k in "${sorted_keys[@]}"; do
escaped_disp=$(echo "${tools_disp[$k]}" | sed 's/"/\\"/g')
echo " [$k]=\"$escaped_disp\""
done
echo ")"
echo ""
# Format keys output as space-separated list in array declaration format
echo "INSTALLER_KEYS=(${sorted_keys[*]})"
} > "$REGISTRY_FILE"
echo "==> registry.sh successfully generated with ${#sorted_keys[@]} tools."

View File

@@ -4,6 +4,12 @@
set -euo pipefail
# Generate the registry dynamically and stage it
if [ -f "./scripts/generate_registry.sh" ]; then
./scripts/generate_registry.sh
git add registry.sh
fi
VERSION_FILE="VERSION"
if [ ! -f "$VERSION_FILE" ]; then