359 lines
11 KiB
Plaintext
359 lines
11 KiB
Plaintext
![]() |
#!/usr/bin/env nu
|
||
|
|
||
|
# Plugin Status Dashboard
|
||
|
# Shows the current status of all plugins and their upstream sync state
|
||
|
|
||
|
# Version check - mandatory for all plugin operations
|
||
|
def version_check [] {
|
||
|
try {
|
||
|
nu scripts/check_version.nu --quiet | ignore
|
||
|
} catch {
|
||
|
print "❌ Nushell version mismatch detected!"
|
||
|
print "🔧 Run: nu scripts/check_version.nu --fix"
|
||
|
exit 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Load plugin registry
|
||
|
def load_registry [] {
|
||
|
let registry_path = "etc/plugin_registry.toml"
|
||
|
if not ($registry_path | path exists) {
|
||
|
error make {msg: "Plugin registry not found. Please run from repository root directory."}
|
||
|
}
|
||
|
open $registry_path
|
||
|
}
|
||
|
|
||
|
# Load exclusions configuration
|
||
|
def load_exclusions [] {
|
||
|
let exclude_path = "etc/upstream_exclude.toml"
|
||
|
if ($exclude_path | path exists) {
|
||
|
let config = open $exclude_path
|
||
|
{
|
||
|
all: ($config.exclude.plugins? | default []),
|
||
|
check: ($config.exclude.check.plugins? | default []),
|
||
|
merge: ($config.exclude.merge.plugins? | default []),
|
||
|
patterns: ($config.exclude.patterns.plugins? | default [])
|
||
|
}
|
||
|
} else {
|
||
|
{all: [], check: [], merge: [], patterns: []}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Check if plugin should be excluded
|
||
|
def is_plugin_excluded [plugin_name: string, exclusions: record] {
|
||
|
# Check direct exclusions
|
||
|
if $plugin_name in $exclusions.all {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
# Check pattern exclusions
|
||
|
for pattern in $exclusions.patterns {
|
||
|
if ($plugin_name | str contains ($pattern | str replace "*" "")) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
false
|
||
|
}
|
||
|
|
||
|
# Filter plugins to only include existing directories and non-excluded plugins
|
||
|
def filter_plugins [plugins: record, exclusions: record] {
|
||
|
$plugins
|
||
|
| transpose name config
|
||
|
| where {
|
||
|
let plugin_name = $in.name
|
||
|
let plugin_path = $in.config.local_path
|
||
|
|
||
|
# Check if directory exists
|
||
|
let dir_exists = ($plugin_path | path exists)
|
||
|
|
||
|
# Check if plugin is excluded
|
||
|
let is_excluded = (is_plugin_excluded $plugin_name $exclusions)
|
||
|
|
||
|
$dir_exists and not $is_excluded
|
||
|
}
|
||
|
| reduce -f {} {|it, acc|
|
||
|
$acc | insert $it.name $it.config
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Save plugin registry
|
||
|
def save_registry [registry: record] {
|
||
|
$registry | to toml | save -f "etc/plugin_registry.toml"
|
||
|
}
|
||
|
|
||
|
# Get status emoji and description
|
||
|
def get_status_emoji [status: string] {
|
||
|
match $status {
|
||
|
"ok" => "✅",
|
||
|
"pending" => "⚠️",
|
||
|
"error" => "❌",
|
||
|
"local_only" => "🏠",
|
||
|
"unknown" => "❓",
|
||
|
"conflict" => "🔥",
|
||
|
_ => "📦"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get status description
|
||
|
def get_status_description [status: string] {
|
||
|
match $status {
|
||
|
"ok" => "Synchronized with upstream",
|
||
|
"pending" => "Changes detected - needs review",
|
||
|
"error" => "Error during upstream check",
|
||
|
"local_only" => "No upstream - local development",
|
||
|
"unknown" => "Not yet checked",
|
||
|
"conflict" => "Merge conflicts detected",
|
||
|
_ => "Unknown status"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Format date for display
|
||
|
def format_date [date_str: string] {
|
||
|
if ($date_str | is-empty) {
|
||
|
"Never"
|
||
|
} else {
|
||
|
try {
|
||
|
$date_str | str substring 0..9 # Show just the date part
|
||
|
} catch {
|
||
|
$date_str
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get plugin directory status
|
||
|
def get_directory_status [local_path: string] {
|
||
|
let full_path = $local_path
|
||
|
if ($full_path | path exists) {
|
||
|
if ($"($full_path)/Cargo.toml" | path exists) {
|
||
|
"📂 Present"
|
||
|
} else {
|
||
|
"📁 No Cargo.toml"
|
||
|
}
|
||
|
} else {
|
||
|
"❌ Missing"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Display summary statistics
|
||
|
def show_summary [plugins: record] {
|
||
|
print "📊 Plugin Status Summary"
|
||
|
print "=================================================="
|
||
|
|
||
|
let status_counts = $plugins
|
||
|
| values
|
||
|
| group-by status
|
||
|
| transpose status plugins
|
||
|
| each {|entry| {
|
||
|
status: $entry.status,
|
||
|
count: ($entry.plugins | length),
|
||
|
emoji: (get_status_emoji $entry.status)
|
||
|
}}
|
||
|
| sort-by count --reverse
|
||
|
|
||
|
let total_plugins = ($plugins | values | length)
|
||
|
print $"📦 Total Plugins: ($total_plugins)"
|
||
|
print ""
|
||
|
|
||
|
for status in $status_counts {
|
||
|
let description = get_status_description $status.status
|
||
|
print $"($status.emoji) ($description): ($status.count) plugins"
|
||
|
}
|
||
|
|
||
|
print ""
|
||
|
|
||
|
# Show breakdown by type
|
||
|
let upstream_plugins = $plugins | values | where ($it.upstream_url | is-not-empty) | length
|
||
|
let local_plugins = $plugins | values | where ($it.upstream_url | is-empty) | length
|
||
|
|
||
|
print "📋 Plugin Types:"
|
||
|
print $" 🔗 With Upstream: ($upstream_plugins) plugins"
|
||
|
print $" 🏠 Local Only: ($local_plugins) plugins"
|
||
|
print ""
|
||
|
}
|
||
|
|
||
|
# Show detailed plugin table
|
||
|
def show_plugin_table [plugins: record, show_all: bool] {
|
||
|
let plugin_data = $plugins
|
||
|
| transpose name config
|
||
|
| each {|plugin|
|
||
|
{
|
||
|
Plugin: $plugin.name,
|
||
|
Status: $"(get_status_emoji $plugin.config.status) ($plugin.config.status)",
|
||
|
"Last Check": (format_date $plugin.config.last_checked_date),
|
||
|
"Directory": (get_directory_status $plugin.config.local_path),
|
||
|
"Upstream": (if ($plugin.config.upstream_url | is-empty) { "None" } else { "📡 Yes" }),
|
||
|
"Auto-OK": (if $plugin.config.auto_ok_on_nu_deps_only { "✓" } else { "✗" }),
|
||
|
"Description": $plugin.config.description
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let filtered_data = if $show_all {
|
||
|
$plugin_data
|
||
|
} else {
|
||
|
$plugin_data | where Status !~ "local_only"
|
||
|
}
|
||
|
|
||
|
$filtered_data | table --expand
|
||
|
}
|
||
|
|
||
|
# Show plugins that need attention
|
||
|
def show_attention_needed [plugins: record] {
|
||
|
let needs_attention = $plugins
|
||
|
| transpose name config
|
||
|
| where $it.config.status in ["pending", "error", "conflict", "unknown"]
|
||
|
|
||
|
if ($needs_attention | length) > 0 {
|
||
|
print "\n🚨 Plugins Requiring Attention:"
|
||
|
print "========================================"
|
||
|
|
||
|
for plugin in $needs_attention {
|
||
|
let emoji = get_status_emoji $plugin.config.status
|
||
|
print $"($emoji) ($plugin.name): ($plugin.config.status)"
|
||
|
if not ($plugin.config.upstream_url | is-empty) {
|
||
|
print $" Upstream: ($plugin.config.upstream_url)"
|
||
|
}
|
||
|
if not ($plugin.config.last_checked_date | is-empty) {
|
||
|
print $" Last checked: ($plugin.config.last_checked_date)"
|
||
|
}
|
||
|
print ""
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Show recent activity
|
||
|
def show_recent_activity [plugins: record] {
|
||
|
let recent_plugins = $plugins
|
||
|
| transpose name config
|
||
|
| where not ($it.config.last_checked_date | is-empty)
|
||
|
| sort-by config.last_checked_date --reverse
|
||
|
| first 5
|
||
|
|
||
|
if ($recent_plugins | length) > 0 {
|
||
|
print "\n📅 Recently Checked Plugins:"
|
||
|
print "=" * 35
|
||
|
|
||
|
for plugin in $recent_plugins {
|
||
|
let emoji = get_status_emoji $plugin.config.status
|
||
|
print $"($emoji) ($plugin.name) - ($plugin.config.last_checked_date)"
|
||
|
}
|
||
|
print ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Update plugin status manually
|
||
|
def update_plugin_status [plugin_name: string, new_status: string] {
|
||
|
let registry = load_registry
|
||
|
|
||
|
if not ($plugin_name in ($registry.plugins | columns)) {
|
||
|
error make {msg: $"Plugin '($plugin_name)' not found in registry"}
|
||
|
}
|
||
|
|
||
|
let valid_statuses = ["ok", "pending", "error", "conflict", "unknown", "local_only"]
|
||
|
if not ($new_status in $valid_statuses) {
|
||
|
error make {msg: $"Invalid status. Valid options: ($valid_statuses | str join ', ')"}
|
||
|
}
|
||
|
|
||
|
let updated_registry = $registry | upsert $"plugins.($plugin_name).status" $new_status
|
||
|
save_registry $updated_registry
|
||
|
|
||
|
print $"✅ Updated ($plugin_name) status to '($new_status)'"
|
||
|
}
|
||
|
|
||
|
# Show help information
|
||
|
def show_help [] {
|
||
|
print "Plugin Status Dashboard - Nushell Plugin Upstream Tracker"
|
||
|
print ""
|
||
|
print "USAGE:"
|
||
|
print " nu plugin_status.nu [OPTIONS] [COMMAND]"
|
||
|
print ""
|
||
|
print "OPTIONS:"
|
||
|
print " --all, -a Show all plugins including local-only"
|
||
|
print " --json, -j Output in JSON format"
|
||
|
print " --help, -h Show this help message"
|
||
|
print ""
|
||
|
print "COMMANDS:"
|
||
|
print " status Show plugin status dashboard (default)"
|
||
|
print " update <plugin> <status> Update plugin status manually"
|
||
|
print " summary Show summary statistics only"
|
||
|
print " attention Show only plugins needing attention"
|
||
|
print ""
|
||
|
print "STATUS VALUES:"
|
||
|
print " ✅ ok - Synchronized with upstream"
|
||
|
print " ⚠️ pending - Changes detected - needs review"
|
||
|
print " ❌ error - Error during upstream check"
|
||
|
print " 🔥 conflict - Merge conflicts detected"
|
||
|
print " ❓ unknown - Not yet checked"
|
||
|
print " 🏠 local_only - No upstream - local development"
|
||
|
print ""
|
||
|
print "EXAMPLES:"
|
||
|
print " nu plugin_status.nu # Show status dashboard"
|
||
|
print " nu plugin_status.nu --all # Show all plugins"
|
||
|
print " nu plugin_status.nu summary # Show summary only"
|
||
|
print " nu plugin_status.nu update highlight ok # Mark plugin as OK"
|
||
|
}
|
||
|
|
||
|
# Main function
|
||
|
def main [
|
||
|
command?: string # Command to execute
|
||
|
plugin?: string # Plugin name (for update command)
|
||
|
status?: string # New status (for update command)
|
||
|
--all (-a) # Show all plugins including local-only
|
||
|
--json (-j) # Output in JSON format
|
||
|
--help (-h) # Show help
|
||
|
] {
|
||
|
if $help {
|
||
|
show_help
|
||
|
return
|
||
|
}
|
||
|
|
||
|
# Mandatory version check before any plugin operations
|
||
|
version_check
|
||
|
|
||
|
# Ensure we're in the repository root directory
|
||
|
if not ("nu_plugin_clipboard" | path exists) {
|
||
|
error make {msg: "Please run this script from the nushell-plugins repository root directory"}
|
||
|
}
|
||
|
|
||
|
# Load registry and exclusions
|
||
|
let registry = load_registry
|
||
|
let exclusions = load_exclusions
|
||
|
let filtered_plugins = filter_plugins $registry.plugins $exclusions
|
||
|
|
||
|
match ($command | default "status") {
|
||
|
"status" => {
|
||
|
if $json {
|
||
|
$filtered_plugins | to json
|
||
|
} else {
|
||
|
show_summary $filtered_plugins
|
||
|
show_plugin_table $filtered_plugins $all
|
||
|
show_attention_needed $filtered_plugins
|
||
|
show_recent_activity $filtered_plugins
|
||
|
|
||
|
print $"\n💡 Tips:"
|
||
|
print " • Run 'nu check_upstream_changes.nu' to check for updates"
|
||
|
print " • Use 'nu plugin_status.nu --all' to see all plugins"
|
||
|
print " • Use 'nu plugin_status.nu update <plugin> <status>' to manually update status"
|
||
|
}
|
||
|
},
|
||
|
"summary" => {
|
||
|
show_summary $filtered_plugins
|
||
|
},
|
||
|
"attention" => {
|
||
|
show_attention_needed $filtered_plugins
|
||
|
},
|
||
|
"update" => {
|
||
|
if ($plugin | is-empty) or ($status | is-empty) {
|
||
|
error make {msg: "Update command requires plugin name and status. Use: update <plugin> <status>"}
|
||
|
}
|
||
|
update_plugin_status $plugin $status
|
||
|
},
|
||
|
_ => {
|
||
|
error make {msg: $"Unknown command: ($command). Use --help for usage information."}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") {
|
||
|
main
|
||
|
}
|