357 lines
11 KiB
Plaintext
357 lines
11 KiB
Plaintext
![]() |
#!/usr/bin/env nu
|
||
|
|
||
|
# Check Upstream Changes Script
|
||
|
# Automatically checks for upstream changes and marks plugins as OK when only nu_* dependencies differ
|
||
|
|
||
|
use lib/cargo_toml_diff.nu *
|
||
|
|
||
|
# 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, operation: string, exclusions: record] {
|
||
|
# Check direct exclusions
|
||
|
if $plugin_name in $exclusions.all {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
# Check operation-specific exclusions
|
||
|
let op_exclusions = match $operation {
|
||
|
"check" => $exclusions.check,
|
||
|
"merge" => $exclusions.merge,
|
||
|
_ => []
|
||
|
}
|
||
|
|
||
|
if $plugin_name in $op_exclusions {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
# Check pattern exclusions
|
||
|
for pattern in $exclusions.patterns {
|
||
|
if ($plugin_name | str contains ($pattern | str replace "*" "")) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
false
|
||
|
}
|
||
|
|
||
|
# Save plugin registry
|
||
|
def save_registry [registry: record] {
|
||
|
$registry | to toml | save -f "etc/plugin_registry.toml"
|
||
|
}
|
||
|
|
||
|
# Get current date in ISO format
|
||
|
def current_date [] {
|
||
|
date now | format date "%Y-%m-%d %H:%M:%S"
|
||
|
}
|
||
|
|
||
|
# Add or update git remote for upstream
|
||
|
def setup_upstream_remote [plugin_path: string, upstream_url: string] {
|
||
|
if ($upstream_url | is-empty) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cd $plugin_path
|
||
|
|
||
|
# Check if upstream remote exists
|
||
|
let remotes = git remote | lines
|
||
|
if not ("upstream" in $remotes) {
|
||
|
print $"Adding upstream remote for ($plugin_path)..."
|
||
|
git remote add upstream $upstream_url
|
||
|
} else {
|
||
|
# Update upstream URL if it changed
|
||
|
let current_url = git remote get-url upstream | str trim
|
||
|
if $current_url != $upstream_url {
|
||
|
print $"Updating upstream remote URL for ($plugin_path)..."
|
||
|
git remote set-url upstream $upstream_url
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Fetch upstream changes without merging
|
||
|
def fetch_upstream [plugin_path: string, upstream_branch: string] {
|
||
|
cd $plugin_path
|
||
|
|
||
|
try {
|
||
|
print $"Fetching upstream changes for ($plugin_path)..."
|
||
|
git fetch upstream $upstream_branch
|
||
|
true
|
||
|
} catch {
|
||
|
print $"Failed to fetch upstream for ($plugin_path)"
|
||
|
false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get latest upstream commit hash
|
||
|
def get_upstream_commit [plugin_path: string, upstream_branch: string] {
|
||
|
cd $plugin_path
|
||
|
|
||
|
try {
|
||
|
git rev-parse $"upstream/($upstream_branch)" | str trim
|
||
|
} catch {
|
||
|
""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Check if there are differences between local and upstream
|
||
|
def has_upstream_changes [plugin_path: string, upstream_branch: string] {
|
||
|
cd $plugin_path
|
||
|
|
||
|
try {
|
||
|
let diff_output = git diff $"HEAD..upstream/($upstream_branch)" --name-only | lines
|
||
|
($diff_output | length) > 0
|
||
|
} catch {
|
||
|
false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Create temporary upstream version of Cargo.toml for comparison
|
||
|
def create_upstream_cargo_toml [plugin_path: string, upstream_branch: string] {
|
||
|
cd $plugin_path
|
||
|
|
||
|
let temp_file = "Cargo.toml.upstream"
|
||
|
try {
|
||
|
git show $"upstream/($upstream_branch):Cargo.toml" | save $temp_file
|
||
|
$temp_file
|
||
|
} catch {
|
||
|
""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Check for source code changes (non-Cargo.toml)
|
||
|
def has_source_changes [plugin_path: string, upstream_branch: string] {
|
||
|
cd $plugin_path
|
||
|
|
||
|
try {
|
||
|
let changed_files = git diff $"HEAD..upstream/($upstream_branch)" --name-only | lines
|
||
|
let source_changes = $changed_files | where ($it =~ "\\.rs$") or ($it == "README.md") or ($it == "LICENSE")
|
||
|
($source_changes | length) > 0
|
||
|
} catch {
|
||
|
false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Process a single plugin
|
||
|
def process_plugin [plugin_name: string, plugin_config: record, nu_managed_deps: list<string>, exclusions: record] {
|
||
|
print $"\n🔍 Checking ($plugin_name)..."
|
||
|
|
||
|
# Check if plugin is excluded
|
||
|
if (is_plugin_excluded $plugin_name "check" $exclusions) {
|
||
|
print $" 🚫 Skipping ($plugin_name) - excluded from upstream checking"
|
||
|
return $plugin_config
|
||
|
}
|
||
|
|
||
|
let plugin_path = $plugin_config.local_path
|
||
|
|
||
|
# Skip if no upstream URL
|
||
|
if ($plugin_config.upstream_url | is-empty) {
|
||
|
print $" ⏭️ Skipping ($plugin_name) - no upstream URL"
|
||
|
return $plugin_config
|
||
|
}
|
||
|
|
||
|
# Check if plugin directory exists
|
||
|
if not ($plugin_path | path exists) {
|
||
|
print $" ❌ Plugin directory not found: ($plugin_path)"
|
||
|
return ($plugin_config | upsert status "error")
|
||
|
}
|
||
|
|
||
|
# Setup upstream remote
|
||
|
setup_upstream_remote $plugin_path $plugin_config.upstream_url
|
||
|
|
||
|
# Fetch upstream changes
|
||
|
if not (fetch_upstream $plugin_path $plugin_config.upstream_branch) {
|
||
|
print $" ❌ Failed to fetch upstream changes"
|
||
|
return ($plugin_config | upsert status "error")
|
||
|
}
|
||
|
|
||
|
# Get latest upstream commit
|
||
|
let upstream_commit = get_upstream_commit $plugin_path $plugin_config.upstream_branch
|
||
|
if ($upstream_commit | is-empty) {
|
||
|
print $" ❌ Could not get upstream commit hash"
|
||
|
return ($plugin_config | upsert status "error")
|
||
|
}
|
||
|
|
||
|
# Check if commit changed since last check
|
||
|
if $plugin_config.last_checked_commit == $upstream_commit {
|
||
|
print $" ✅ No new upstream changes since last check"
|
||
|
return ($plugin_config | upsert last_checked_date (current_date))
|
||
|
}
|
||
|
|
||
|
# Check if there are any changes
|
||
|
if not (has_upstream_changes $plugin_path $plugin_config.upstream_branch) {
|
||
|
print $" ✅ Local is up to date with upstream"
|
||
|
return ($plugin_config
|
||
|
| upsert status "ok"
|
||
|
| upsert last_checked_commit $upstream_commit
|
||
|
| upsert last_checked_date (current_date))
|
||
|
}
|
||
|
|
||
|
print $" 📋 Changes detected, analyzing..."
|
||
|
|
||
|
# Check for source code changes
|
||
|
let has_source = has_source_changes $plugin_path $plugin_config.upstream_branch
|
||
|
if $has_source {
|
||
|
print $" ⚠️ Source code changes detected - requires manual review"
|
||
|
return ($plugin_config
|
||
|
| upsert status "pending"
|
||
|
| upsert last_checked_commit $upstream_commit
|
||
|
| upsert last_checked_date (current_date))
|
||
|
}
|
||
|
|
||
|
# Create upstream Cargo.toml for comparison
|
||
|
let upstream_cargo_path = create_upstream_cargo_toml $plugin_path $plugin_config.upstream_branch
|
||
|
if ($upstream_cargo_path | is-empty) {
|
||
|
print $" ❌ Could not extract upstream Cargo.toml"
|
||
|
return ($plugin_config | upsert status "error")
|
||
|
}
|
||
|
|
||
|
# Compare Cargo.toml files
|
||
|
let local_cargo = $"($plugin_path)/Cargo.toml"
|
||
|
try {
|
||
|
let diff_result = compare_cargo_tomls $local_cargo $"($plugin_path)/($upstream_cargo_path)"
|
||
|
|
||
|
# Clean up temporary file
|
||
|
rm $"($plugin_path)/($upstream_cargo_path)"
|
||
|
|
||
|
if not $diff_result.analysis.has_changes {
|
||
|
print $" ✅ No meaningful changes in Cargo.toml"
|
||
|
return ($plugin_config
|
||
|
| upsert status "ok"
|
||
|
| upsert last_checked_commit $upstream_commit
|
||
|
| upsert last_checked_date (current_date))
|
||
|
}
|
||
|
|
||
|
# Check if safe for auto-OK
|
||
|
if ($plugin_config.auto_ok_on_nu_deps_only) and (is_safe_for_auto_ok $diff_result $nu_managed_deps) {
|
||
|
print $" ✅ AUTO-OK: Only nu_* dependency changes detected"
|
||
|
let summary = generate_diff_summary $diff_result
|
||
|
print $" Changes:\n($summary | str replace --all '\n' '\n ')"
|
||
|
|
||
|
return ($plugin_config
|
||
|
| upsert status "ok"
|
||
|
| upsert last_checked_commit $upstream_commit
|
||
|
| upsert last_checked_date (current_date))
|
||
|
} else {
|
||
|
print $" ⚠️ PENDING: Non-nu dependency changes require manual review"
|
||
|
let summary = generate_diff_summary $diff_result
|
||
|
print $" Changes:\n($summary | str replace --all '\n' '\n ')"
|
||
|
|
||
|
return ($plugin_config
|
||
|
| upsert status "pending"
|
||
|
| upsert last_checked_commit $upstream_commit
|
||
|
| upsert last_checked_date (current_date))
|
||
|
}
|
||
|
} catch {|err|
|
||
|
print $" ❌ Error comparing Cargo.toml files: ($err.msg)"
|
||
|
# Clean up on error
|
||
|
try { rm $"($plugin_path)/($upstream_cargo_path)" } catch { }
|
||
|
return ($plugin_config | upsert status "error")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Main function
|
||
|
def main [
|
||
|
--plugin (-p): string = "" # Check specific plugin only
|
||
|
--verbose (-v) # Verbose output
|
||
|
] {
|
||
|
# Mandatory version check before any plugin operations
|
||
|
version_check
|
||
|
|
||
|
print "🚀 Checking upstream changes for nushell plugins..."
|
||
|
|
||
|
# 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
|
||
|
|
||
|
# Get managed nu dependencies
|
||
|
let nu_managed_deps = $registry.settings.nu_managed_dependencies
|
||
|
|
||
|
# Process plugins
|
||
|
mut updated_registry = $registry
|
||
|
|
||
|
let plugins_to_check = if ($plugin | is-empty) {
|
||
|
$registry.plugins | transpose key value
|
||
|
} else {
|
||
|
if $plugin in ($registry.plugins | columns) {
|
||
|
[{key: $plugin, value: ($registry.plugins | get $plugin)}]
|
||
|
} else {
|
||
|
error make {msg: $"Plugin '($plugin)' not found in registry"}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for plugin_entry in $plugins_to_check {
|
||
|
let plugin_name = $plugin_entry.key
|
||
|
let plugin_config = $plugin_entry.value
|
||
|
|
||
|
let updated_config = process_plugin $plugin_name $plugin_config $nu_managed_deps $exclusions
|
||
|
$updated_registry = ($updated_registry | upsert $"plugins.($plugin_name)" $updated_config)
|
||
|
}
|
||
|
|
||
|
# Save updated registry
|
||
|
save_registry $updated_registry
|
||
|
|
||
|
print "\n📊 Summary:"
|
||
|
let status_counts = $updated_registry.plugins
|
||
|
| values
|
||
|
| group-by status
|
||
|
| transpose status count
|
||
|
| each {|entry| {status: $entry.status, count: ($entry.count | length)}}
|
||
|
|
||
|
for status in $status_counts {
|
||
|
let emoji = match $status.status {
|
||
|
"ok" => "✅",
|
||
|
"pending" => "⚠️",
|
||
|
"error" => "❌",
|
||
|
"local_only" => "🏠",
|
||
|
"unknown" => "❓",
|
||
|
_ => "📦"
|
||
|
}
|
||
|
print $" ($emoji) ($status.status): ($status.count) plugins"
|
||
|
}
|
||
|
|
||
|
print $"\n🔄 Registry updated: etc/plugin_registry.toml"
|
||
|
print "💡 Run 'bash scripts/run.sh plugin_status.nu' to see detailed status"
|
||
|
}
|
||
|
|
||
|
if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") {
|
||
|
main
|
||
|
}
|