While json.sh worked decently for reading json files, I didn't want to implement writing to json files as well and make it completely unreadable due to the added complexity. So, I think its better to just use jq and keep things relatively simple with the tradeoff of a lightweight dependency
181 lines
5.9 KiB
Bash
181 lines
5.9 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Parses a plugin manifest using jq and outputs bash array assignments
|
|
parse_plugin_manifest() {
|
|
jq -r '
|
|
.plugins | to_entries[] |
|
|
(if .value.version then "PLUGIN_VERSIONS[\"" + .key + "\"]=\"" + .value.version + "\"" else empty end),
|
|
(if .value.url then "PLUGIN_URLS[\"" + .key + "\"]=\"" + .value.url + "\"" else empty end),
|
|
(if .value.bootstrap then "PLUGIN_BOOTSTRAP_VERSIONS[\"" + .key + "\"]=\"" + .value.bootstrap + "\"" else empty end)
|
|
'
|
|
}
|
|
|
|
# Ensures that the plugin sources file exists, initializing it with the official repository by default
|
|
ensure_sources_file() {
|
|
local sources_file="$BOOTSTRAP_DIR/plugin_sources.txt"
|
|
if [ ! -f "$sources_file" ]; then
|
|
mkdir -p "$BOOTSTRAP_DIR"
|
|
echo "# Add raw URLs to JSON plugin manifests here, one per line." > "$sources_file"
|
|
echo "# Official Bootstrap plugin repository" >> "$sources_file"
|
|
echo "https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master/plugins.json" >> "$sources_file"
|
|
fi
|
|
}
|
|
|
|
# Fetches manifests from sources and generates the cache
|
|
update_plugin_cache() {
|
|
ensure_sources_file
|
|
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
|
|
declare -g -A PLUGIN_BOOTSTRAP_VERSIONS
|
|
EOF
|
|
|
|
if [ -f "$sources_file" ]; then
|
|
local dl_args=()
|
|
local temp_manifests=()
|
|
|
|
while IFS= read -r url || [ -n "$url" ]; do
|
|
# Skip empty lines and comments
|
|
[[ -z "$url" || "$url" == \#* ]] && continue
|
|
|
|
local temp_file
|
|
temp_file=$(mktemp --suffix=".json" 2>/dev/null || mktemp)
|
|
dl_args+=("$url" "$temp_file")
|
|
temp_manifests+=("$temp_file")
|
|
done < "$sources_file"
|
|
|
|
if [ ${#dl_args[@]} -gt 0 ]; then
|
|
log_info "Fetching ${#temp_manifests[@]} plugin manifests in parallel..."
|
|
download_multiple_files_parallel "${dl_args[@]}"
|
|
|
|
for temp_file in "${temp_manifests[@]}"; do
|
|
if [ -s "$temp_file" ]; then
|
|
cat "$temp_file" | parse_plugin_manifest >> "$cache_file"
|
|
fi
|
|
rm -f "$temp_file"
|
|
done
|
|
fi
|
|
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() {
|
|
ensure_sources_file
|
|
local sources_file="$BOOTSTRAP_DIR/plugin_sources.txt"
|
|
|
|
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 is_ephemeral=false
|
|
local cmd_args=()
|
|
for arg in "$@"; do
|
|
if [ "$arg" = "-e" ] || [ "$arg" = "--ephemeral" ]; then
|
|
is_ephemeral=true
|
|
else
|
|
cmd_args+=("$arg")
|
|
fi
|
|
done
|
|
|
|
local url="${PLUGIN_URLS[$plugin_name]:-}"
|
|
if [ -z "$url" ]; then
|
|
log_error "Plugin '$plugin_name' not found in cache."
|
|
return 1
|
|
fi
|
|
|
|
# Check compatibility version
|
|
local compat_ver="${PLUGIN_BOOTSTRAP_VERSIONS[$plugin_name]:-}"
|
|
if [ -n "$compat_ver" ]; then
|
|
local current_ver="0.0.0"
|
|
if [ -f "$BOOTSTRAP_DIR/VERSION" ]; then
|
|
current_ver=$(cat "$BOOTSTRAP_DIR/VERSION" | tr -d '[:space:]')
|
|
fi
|
|
if version_lt "$compat_ver" "$current_ver"; then
|
|
log_warn "Plugin '$plugin_name' is only tested up to bootstrap version $compat_ver (current: $current_ver). It may be incompatible."
|
|
fi
|
|
fi
|
|
|
|
if [ "$is_ephemeral" = "true" ]; then
|
|
log_info "Downloading and running plugin '$plugin_name' (ephemeral)..."
|
|
local script_content
|
|
if ! script_content=$(curl -fsSL "$url"); then
|
|
log_error "Failed to download plugin '$plugin_name' from $url"
|
|
return 1
|
|
fi
|
|
|
|
# Execute the plugin directly in memory in a subshell
|
|
(
|
|
export BOOTSTRAP_DIR
|
|
# We use bash -c and pass the script content to keep stdin free for interactive plugins
|
|
# The "$0" arg for bash -c is set to the plugin name
|
|
bash -c "$script_content" "$plugin_name" "${cmd_args[@]}"
|
|
)
|
|
return $?
|
|
else
|
|
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
|
|
(
|
|
export BOOTSTRAP_DIR
|
|
bash "$local_plugin" "${cmd_args[@]}"
|
|
)
|
|
return $?
|
|
fi
|
|
}
|