From 9c86486ee6ae690768bdf45f8ab55145f39d6e0a Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 25 Jun 2026 19:20:09 +0530 Subject: [PATCH] feat: Added support for lazy loading plugins --- bootstrap.sh | 4 +- commands/up.sh | 7 +++ lib/plugins.sh | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/routes.sh | 24 +++++++- 4 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 lib/plugins.sh diff --git a/bootstrap.sh b/bootstrap.sh index c6f422c..8aac6c8 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -41,7 +41,7 @@ else BOOTSTRAP_SOURCE_DIR="$BOOTSTRAP_TMP_DIR" _BASE_URL="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master" - _LIBS=("lib/common.sh" "lib/rollback.sh" "lib/platform.sh" "lib/shell_config.sh") + _LIBS=("lib/common.sh" "lib/rollback.sh" "lib/platform.sh" "lib/shell_config.sh" "lib/json.sh" "lib/plugins.sh") _curl_args=() for _lib in "${_LIBS[@]}"; do @@ -82,6 +82,8 @@ install_bootstrap() { "lib/rollback.sh" "lib/platform.sh" "lib/shell_config.sh" + "lib/json.sh" + "lib/plugins.sh" "commands/help.sh" "commands/con.sh" "commands/uninstall.sh" diff --git a/commands/up.sh b/commands/up.sh index 50c7cbb..32f0bf4 100644 --- a/commands/up.sh +++ b/commands/up.sh @@ -48,6 +48,13 @@ if version_lt "$local_ver" "$remote_ver" || [ "$force_update" = true ]; then if bash "$tmp_bootstrap"; then # Update the last update timestamp date +%s > "${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/.last_b_update" 2>/dev/null || true + + # Update plugin cache + if [ -f "${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/lib/plugins.sh" ]; then + . "${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/lib/plugins.sh" + update_plugin_cache + fi + log_success "Bootstrap CLI successfully updated to version $remote_ver!" else log_error "Failed to execute bootstrap installer." diff --git a/lib/plugins.sh b/lib/plugins.sh new file mode 100644 index 0000000..87559ff --- /dev/null +++ b/lib/plugins.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash + +if [ -f "$BOOTSTRAP_DIR/lib/json.sh" ]; then + . "$BOOTSTRAP_DIR/lib/json.sh" +fi + +# Parses a plugin manifest using the generic json parser and outputs bash array assignments +parse_plugin_manifest() { + # The generic parser outputs lines like: + # plugins.myplugin.version="1.0" + # plugins.myplugin.url="https://..." + # We want to extract myplugin and the keys to build: + # PLUGIN_VERSIONS["myplugin"]="1.0" + # PLUGIN_URLS["myplugin"]="https://..." + + parse_json | awk -F'=' ' + { + path = $1 + val = $2 + + # Remove quotes around value for bash array assignment + gsub(/^"|"$/, "", val) + + # Match paths starting with "plugins." + if (match(path, /^plugins\./)) { + rest = substr(path, RLENGTH + 1) + # Find the last dot to separate plugin name from the property key + last_dot = 0 + for (i=length(rest); i>0; i--) { + if (substr(rest, i, 1) == ".") { + last_dot = i + break + } + } + if (last_dot > 0) { + plugin_name = substr(rest, 1, last_dot - 1) + prop = substr(rest, last_dot + 1) + if (prop == "version") { + print "PLUGIN_VERSIONS[\"" plugin_name "\"]=\"" val "\"" + } else if (prop == "url") { + print "PLUGIN_URLS[\"" plugin_name "\"]=\"" val "\"" + } + } + } + }' +} + +# Fetches manifests from sources and generates the cache +update_plugin_cache() { + local cache_file="$BOOTSTRAP_DIR/lib/plugin_cache.sh" + local sources_file="$BOOTSTRAP_DIR/plugin_sources.txt" + + mkdir -p "$BOOTSTRAP_DIR/lib" + + # Initialize cache file + cat << 'EOF' > "$cache_file" +# Auto-generated plugin cache. Do not edit manually. +declare -g -A PLUGIN_URLS +declare -g -A PLUGIN_VERSIONS +EOF + + if [ -f "$sources_file" ]; then + while IFS= read -r url || [ -n "$url" ]; do + # Skip empty lines and comments + [[ -z "$url" || "$url" == \#* ]] && continue + + log_info "Fetching plugin manifest from $url..." + local manifest_json + if manifest_json=$(curl -fsSL "$url" 2>/dev/null); then + echo "$manifest_json" | parse_plugin_manifest >> "$cache_file" + else + log_warn "Failed to fetch manifest from $url" + fi + done < "$sources_file" + fi + + # Clear downloaded scripts to force lazy re-download of the updated versions + rm -rf "$BOOTSTRAP_DIR/plugins" 2>/dev/null || true + + log_success "Plugin cache updated successfully." +} + +manage_plugin_sources() { + local sources_file="$BOOTSTRAP_DIR/plugin_sources.txt" + if [ ! -f "$sources_file" ]; then + touch "$sources_file" + echo "# Add raw URLs to JSON plugin manifests here, one per line." > "$sources_file" + fi + + local editor="${EDITOR:-}" + if [ -z "$editor" ]; then + if has_command nvim; then editor="nvim" + elif has_command vim; then editor="vim" + elif has_command nano; then editor="nano" + else editor="vi" + fi + fi + + $editor "$sources_file" + + # Update cache after editing + update_plugin_cache +} + +handle_plugin() { + local subcmd="${1:-}" + case "$subcmd" in + sources) + manage_plugin_sources + ;; + update) + update_plugin_cache + ;; + *) + log_error "Unknown plugin command: $subcmd" + log_info "Available commands: b plugin sources, b plugin update" + exit 1 + ;; + esac +} + +run_plugin() { + local plugin_name="$1" + shift + + local url="${PLUGIN_URLS[$plugin_name]:-}" + if [ -z "$url" ]; then + log_error "Plugin '$plugin_name' not found in cache." + return 1 + fi + + local plugin_dir="$BOOTSTRAP_DIR/plugins" + local local_plugin="$plugin_dir/${plugin_name}.sh" + + if [ ! -f "$local_plugin" ]; then + log_info "Downloading plugin '$plugin_name'..." + mkdir -p "$plugin_dir" + if ! curl -fsSL "$url" -o "$local_plugin"; then + log_error "Failed to download plugin '$plugin_name' from $url" + rm -f "$local_plugin" + return 1 + fi + chmod +x "$local_plugin" + fi + + log_info "Running plugin '$plugin_name'..." + # Execute the plugin in a subshell, passing any additional arguments + ( + # Plugins have access to common.sh already because it's sourced in the parent shell + # Export BOOTSTRAP_DIR so plugins can use it + export BOOTSTRAP_DIR + bash "$local_plugin" "$@" + ) + return $? +} + diff --git a/lib/routes.sh b/lib/routes.sh index 8d43cf4..26a8b98 100755 --- a/lib/routes.sh +++ b/lib/routes.sh @@ -35,6 +35,14 @@ else INSTALLER_KEYS=() fi +# Source plugin system +if [ -f "$BOOTSTRAP_DIR/lib/plugins.sh" ]; then + . "$BOOTSTRAP_DIR/lib/plugins.sh" + if [ -f "$BOOTSTRAP_DIR/lib/plugin_cache.sh" ]; then + . "$BOOTSTRAP_DIR/lib/plugin_cache.sh" + fi +fi + # Helper function to run/edit installer scripts run_ware() { local tool="$1" @@ -191,6 +199,12 @@ for script in "${SCRIPTS[@]}"; do else # Handle non-installer commands case "$script" in + plugin) + shift # consume 'plugin' arg + handle_plugin "$@" + # Once handle_plugin completes, we should exit so it doesn't process more SCRIPTS + exit $? + ;; all) if [ -f "$BOOTSTRAP_DIR/commands/help.sh" ]; then . "$BOOTSTRAP_DIR/commands/help.sh" @@ -263,9 +277,13 @@ for script in "${SCRIPTS[@]}"; do exit 0 ;; *) - log_error "Unknown command '$script'." - log_info "Run 'b all' to list all available commands." - exit 1 + if [[ -n "${PLUGIN_URLS[$script]:-}" ]]; then + run_plugin "$script" "$@" + else + log_error "Unknown command '$script'." + log_info "Run 'b all' to list all available commands." + exit 1 + fi ;; esac fi