test: expand test coverage across core functions, enforce strict fish_indent linting, and add network isolation harnesses #143
+4
-5
@@ -3,7 +3,7 @@
|
|||||||
function __bd_debug
|
function __bd_debug
|
||||||
set -l file "$BASH_COMP_DEBUG_FILE"
|
set -l file "$BASH_COMP_DEBUG_FILE"
|
||||||
if test -n "$file"
|
if test -n "$file"
|
||||||
echo "$argv" >> $file
|
echo "$argv" >>$file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -108,7 +108,6 @@ function __bd_requires_order_preservation
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# This function does two things:
|
# This function does two things:
|
||||||
# - Obtain the completions and store them in the global __bd_comp_results
|
# - Obtain the completions and store them in the global __bd_comp_results
|
||||||
# - Return false if file completion should be performed
|
# - Return false if file completion should be performed
|
||||||
@@ -215,18 +214,18 @@ end
|
|||||||
# so we can properly delete any completions provided by another script.
|
# so we can properly delete any completions provided by another script.
|
||||||
# Only do this if the program can be found, or else fish may print some errors; besides,
|
# Only do this if the program can be found, or else fish may print some errors; besides,
|
||||||
# the existing completions will only be loaded if the program can be found.
|
# the existing completions will only be loaded if the program can be found.
|
||||||
if type -q "bd"
|
if type -q bd
|
||||||
# The space after the program name is essential to trigger completion for the program
|
# The space after the program name is essential to trigger completion for the program
|
||||||
# and not completion of the program name itself.
|
# and not completion of the program name itself.
|
||||||
# Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
|
# Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
|
||||||
complete --do-complete "bd " > /dev/null 2>&1
|
complete --do-complete "bd " >/dev/null 2>&1
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove any pre-existing completions for the program since we will be handling all of them.
|
# Remove any pre-existing completions for the program since we will be handling all of them.
|
||||||
complete -c bd -e
|
complete -c bd -e
|
||||||
|
|
||||||
# this will get called after the two calls below and clear the $__bd_perform_completion_once_result global
|
# this will get called after the two calls below and clear the $__bd_perform_completion_once_result global
|
||||||
complete -c bd -n '__bd_clear_perform_completion_once_result'
|
complete -c bd -n __bd_clear_perform_completion_once_result
|
||||||
# The call to __bd_prepare_completions will setup __bd_comp_results
|
# The call to __bd_prepare_completions will setup __bd_comp_results
|
||||||
# which provides the program's completion choices.
|
# which provides the program's completion choices.
|
||||||
# If this doesn't require order preservation, we don't use the -k flag
|
# If this doesn't require order preservation, we don't use the -k flag
|
||||||
|
|||||||
+22
-22
@@ -1,14 +1,14 @@
|
|||||||
# fish completion for copilot
|
# fish completion for copilot
|
||||||
# Generated by `copilot completion fish`. Do not edit by hand.
|
# Generated by `copilot completion fish`. Do not edit by hand.
|
||||||
|
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'login' -d 'Authenticate with Copilot'
|
complete -c copilot -n __fish_use_subcommand -f -a login -d 'Authenticate with Copilot'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'help' -d 'Display help information'
|
complete -c copilot -n __fish_use_subcommand -f -a help -d 'Display help information'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'init' -d 'Initialize Copilot instructions'
|
complete -c copilot -n __fish_use_subcommand -f -a init -d 'Initialize Copilot instructions'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'update' -d 'Download the latest version'
|
complete -c copilot -n __fish_use_subcommand -f -a update -d 'Download the latest version'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'version' -d 'Display version information'
|
complete -c copilot -n __fish_use_subcommand -f -a version -d 'Display version information'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'plugin' -d 'Manage plugins'
|
complete -c copilot -n __fish_use_subcommand -f -a plugin -d 'Manage plugins'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'mcp' -d 'Manage MCP servers'
|
complete -c copilot -n __fish_use_subcommand -f -a mcp -d 'Manage MCP servers'
|
||||||
complete -c copilot -n '__fish_use_subcommand' -f -a 'completion' -d 'Generate a shell completion script'
|
complete -c copilot -n __fish_use_subcommand -f -a completion -d 'Generate a shell completion script'
|
||||||
complete -c copilot -l version -s v -f -d 'show version information'
|
complete -c copilot -l version -s v -f -d 'show version information'
|
||||||
complete -c copilot -l interactive -s i -r -d 'Start interactive mode and automatically execute this prompt'
|
complete -c copilot -l interactive -s i -r -d 'Start interactive mode and automatically execute this prompt'
|
||||||
complete -c copilot -l prompt -s p -r -d 'Execute a prompt in non-interactive mode (exits after completion)'
|
complete -c copilot -l prompt -s p -r -d 'Execute a prompt in non-interactive mode (exits after completion)'
|
||||||
@@ -75,22 +75,22 @@ complete -c copilot -l acp -f -d 'Start as Agent Client Protocol server'
|
|||||||
complete -c copilot -l remote -f -d 'Enable remote control of your session from GitHub web and mobile'
|
complete -c copilot -l remote -f -d 'Enable remote control of your session from GitHub web and mobile'
|
||||||
complete -c copilot -l no-remote -f -d 'Disable remote control of your session from GitHub web and mobile'
|
complete -c copilot -l no-remote -f -d 'Disable remote control of your session from GitHub web and mobile'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from login' -l host -r -d 'GitHub host URL (default: https://github.com)'
|
complete -c copilot -n '__fish_seen_subcommand_from login' -l host -r -d 'GitHub host URL (default: https://github.com)'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a 'install' -d 'Install a plugin'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a install -d 'Install a plugin'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a 'uninstall' -d 'Uninstall a plugin'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a uninstall -d 'Uninstall a plugin'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a 'update' -d 'Update a plugin'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a update -d 'Update a plugin'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a 'list' -d 'List installed plugins'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a list -d 'List installed plugins'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a 'marketplace' -d 'Manage plugin marketplaces'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin' -f -a marketplace -d 'Manage plugin marketplaces'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l all -f -d 'Update all installed plugins'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l all -f -d 'Update all installed plugins'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a 'add' -d 'Add a marketplace'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a add -d 'Add a marketplace'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a 'remove' -d 'Remove a marketplace'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a remove -d 'Remove a marketplace'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a 'list' -d 'List registered marketplaces'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a list -d 'List registered marketplaces'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a 'browse' -d 'Browse plugins in a marketplace'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a browse -d 'Browse plugins in a marketplace'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a 'update' -d 'Update marketplace plugin catalogs'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace' -f -a update -d 'Update marketplace plugin catalogs'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace; and __fish_seen_subcommand_from remove' -l force -s f -f -d 'Force removal even if plugins are installed'
|
complete -c copilot -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from marketplace; and __fish_seen_subcommand_from remove' -l force -s f -f -d 'Force removal even if plugins are installed'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a 'list' -d 'List configured MCP servers'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a list -d 'List configured MCP servers'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a 'get' -d 'Show server details'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a get -d 'Show server details'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a 'add' -d 'Add an MCP server'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a add -d 'Add an MCP server'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a 'remove' -d 'Remove an MCP server'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp' -f -a remove -d 'Remove an MCP server'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from list' -l json -f -d 'Output as JSON'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from list' -l json -f -d 'Output as JSON'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from get' -l json -f -d 'Output as JSON'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from get' -l json -f -d 'Output as JSON'
|
||||||
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from get' -l show-secrets -f -d 'Show full environment variable and header values (masked by default)'
|
complete -c copilot -n '__fish_seen_subcommand_from mcp; and __fish_seen_subcommand_from get' -l show-secrets -f -d 'Show full environment variable and header values (masked by default)'
|
||||||
|
|||||||
+60
-60
@@ -1,38 +1,38 @@
|
|||||||
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
|
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
|
||||||
function __fish_deadbranch_global_optspecs
|
function __fish_deadbranch_global_optspecs
|
||||||
string join \n h/help V/version
|
string join \n h/help V/version
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fish_deadbranch_needs_command
|
function __fish_deadbranch_needs_command
|
||||||
# Figure out if the current invocation already has a command.
|
# Figure out if the current invocation already has a command.
|
||||||
set -l cmd (commandline -opc)
|
set -l cmd (commandline -opc)
|
||||||
set -e cmd[1]
|
set -e cmd[1]
|
||||||
argparse -s (__fish_deadbranch_global_optspecs) -- $cmd 2>/dev/null
|
argparse -s (__fish_deadbranch_global_optspecs) -- $cmd 2>/dev/null
|
||||||
or return
|
or return
|
||||||
if set -q argv[1]
|
if set -q argv[1]
|
||||||
# Also print the command, so this can be used to figure out what it is.
|
# Also print the command, so this can be used to figure out what it is.
|
||||||
echo $argv[1]
|
echo $argv[1]
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fish_deadbranch_using_subcommand
|
function __fish_deadbranch_using_subcommand
|
||||||
set -l cmd (__fish_deadbranch_needs_command)
|
set -l cmd (__fish_deadbranch_needs_command)
|
||||||
test -z "$cmd"
|
test -z "$cmd"
|
||||||
and return 1
|
and return 1
|
||||||
contains -- $cmd[1] $argv
|
contains -- $cmd[1] $argv
|
||||||
end
|
end
|
||||||
|
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -s h -l help -d 'Print help'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -s V -l version -d 'Print version'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "list" -d 'List stale branches'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a list -d 'List stale branches'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "clean" -d 'Delete stale branches (merged only by default, use --force for unmerged)'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a clean -d 'Delete stale branches (merged only by default, use --force for unmerged)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "config" -d 'Manage configuration'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a config -d 'Manage configuration'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "backup" -d 'Manage backups'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a backup -d 'Manage backups'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "stats" -d 'Show repository branch statistics'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a stats -d 'Show repository branch statistics'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "completions" -d 'Generate shell completion scripts'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a completions -d 'Generate shell completion scripts'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n __fish_deadbranch_needs_command -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -s d -l days -d 'Only show branches older than N days (default: from config or 30)' -r
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -s d -l days -d 'Only show branches older than N days (default: from config or 30)' -r
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l local -d 'Only show local branches'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l local -d 'Only show local branches'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l remote -d 'Only show remote branches'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l remote -d 'Only show remote branches'
|
||||||
@@ -51,11 +51,11 @@ complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s h -l hel
|
|||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "set" -d 'Set a configuration value'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a set -d 'Set a configuration value'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "show" -d 'Show current configuration'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a show -d 'Show current configuration'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "edit" -d 'Open config file in $EDITOR'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a edit -d 'Open config file in $EDITOR'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "reset" -d 'Reset configuration to defaults'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a reset -d 'Reset configuration to defaults'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from show" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from show" -s h -l help -d 'Print help'
|
||||||
@@ -64,18 +64,18 @@ complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish
|
|||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from edit" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from edit" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "set" -d 'Set a configuration value'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a set -d 'Set a configuration value'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "show" -d 'Show current configuration'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a show -d 'Show current configuration'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "edit" -d 'Open config file in $EDITOR'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a edit -d 'Open config file in $EDITOR'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "reset" -d 'Reset configuration to defaults'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a reset -d 'Reset configuration to defaults'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "list" -d 'List available backups'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a list -d 'List available backups'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "restore" -d 'Restore a branch from backup'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a restore -d 'Restore a branch from backup'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "stats" -d 'Show backup storage statistics'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a stats -d 'Show backup storage statistics'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "clean" -d 'Remove old backups, keeping the most recent ones'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a clean -d 'Remove old backups, keeping the most recent ones'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l repo -d 'Show backups for a specific repository by name' -r
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l repo -d 'Show backups for a specific repository by name' -r
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l current -d 'Only show backups for current repository'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l current -d 'Only show backups for current repository'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help'
|
||||||
@@ -94,28 +94,28 @@ complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish
|
|||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s y -l yes -d 'Skip confirmation prompt'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s y -l yes -d 'Skip confirmation prompt'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "list" -d 'List available backups'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a list -d 'List available backups'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "restore" -d 'Restore a branch from backup'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a restore -d 'Restore a branch from backup'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "stats" -d 'Show backup storage statistics'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a stats -d 'Show backup storage statistics'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "clean" -d 'Remove old backups, keeping the most recent ones'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a clean -d 'Remove old backups, keeping the most recent ones'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s d -l days -d 'Treat branches older than N days as stale (default: from config or 30)' -r
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s d -l days -d 'Treat branches older than N days as stale (default: from config or 30)' -r
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s h -l help -d 'Print help'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s h -l help -d 'Print help'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s V -l version -d 'Print version'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s V -l version -d 'Print version'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "list" -d 'List stale branches'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a list -d 'List stale branches'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "clean" -d 'Delete stale branches (merged only by default, use --force for unmerged)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a clean -d 'Delete stale branches (merged only by default, use --force for unmerged)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "config" -d 'Manage configuration'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a config -d 'Manage configuration'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "backup" -d 'Manage backups'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a backup -d 'Manage backups'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "stats" -d 'Show repository branch statistics'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a stats -d 'Show repository branch statistics'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "completions" -d 'Generate shell completion scripts'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a completions -d 'Generate shell completion scripts'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a help -d 'Print this message or the help of the given subcommand(s)'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "set" -d 'Set a configuration value'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a set -d 'Set a configuration value'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "show" -d 'Show current configuration'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a show -d 'Show current configuration'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "edit" -d 'Open config file in $EDITOR'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a edit -d 'Open config file in $EDITOR'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "reset" -d 'Reset configuration to defaults'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a reset -d 'Reset configuration to defaults'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "list" -d 'List available backups'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a list -d 'List available backups'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "restore" -d 'Restore a branch from backup'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a restore -d 'Restore a branch from backup'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "stats" -d 'Show backup storage statistics'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a stats -d 'Show backup storage statistics'
|
||||||
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "clean" -d 'Remove old backups, keeping the most recent ones'
|
complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a clean -d 'Remove old backups, keeping the most recent ones'
|
||||||
|
|||||||
+4
-5
@@ -3,7 +3,7 @@
|
|||||||
function __ov_debug
|
function __ov_debug
|
||||||
set -l file "$BASH_COMP_DEBUG_FILE"
|
set -l file "$BASH_COMP_DEBUG_FILE"
|
||||||
if test -n "$file"
|
if test -n "$file"
|
||||||
echo "$argv" >> $file
|
echo "$argv" >>$file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -108,7 +108,6 @@ function __ov_requires_order_preservation
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# This function does two things:
|
# This function does two things:
|
||||||
# - Obtain the completions and store them in the global __ov_comp_results
|
# - Obtain the completions and store them in the global __ov_comp_results
|
||||||
# - Return false if file completion should be performed
|
# - Return false if file completion should be performed
|
||||||
@@ -215,18 +214,18 @@ end
|
|||||||
# so we can properly delete any completions provided by another script.
|
# so we can properly delete any completions provided by another script.
|
||||||
# Only do this if the program can be found, or else fish may print some errors; besides,
|
# Only do this if the program can be found, or else fish may print some errors; besides,
|
||||||
# the existing completions will only be loaded if the program can be found.
|
# the existing completions will only be loaded if the program can be found.
|
||||||
if type -q "ov"
|
if type -q ov
|
||||||
# The space after the program name is essential to trigger completion for the program
|
# The space after the program name is essential to trigger completion for the program
|
||||||
# and not completion of the program name itself.
|
# and not completion of the program name itself.
|
||||||
# Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
|
# Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
|
||||||
complete --do-complete "ov " > /dev/null 2>&1
|
complete --do-complete "ov " >/dev/null 2>&1
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove any pre-existing completions for the program since we will be handling all of them.
|
# Remove any pre-existing completions for the program since we will be handling all of them.
|
||||||
complete -c ov -e
|
complete -c ov -e
|
||||||
|
|
||||||
# this will get called after the two calls below and clear the $__ov_perform_completion_once_result global
|
# this will get called after the two calls below and clear the $__ov_perform_completion_once_result global
|
||||||
complete -c ov -n '__ov_clear_perform_completion_once_result'
|
complete -c ov -n __ov_clear_perform_completion_once_result
|
||||||
# The call to __ov_prepare_completions will setup __ov_comp_results
|
# The call to __ov_prepare_completions will setup __ov_comp_results
|
||||||
# which provides the program's completion choices.
|
# which provides the program's completion choices.
|
||||||
# If this doesn't require order preservation, we don't use the -k flag
|
# If this doesn't require order preservation, we don't use the -k flag
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ end
|
|||||||
function __tailscale_debug
|
function __tailscale_debug
|
||||||
set -l file "$BASH_COMP_DEBUG_FILE"
|
set -l file "$BASH_COMP_DEBUG_FILE"
|
||||||
if test -n "$file"
|
if test -n "$file"
|
||||||
echo "$argv" >> $file
|
echo "$argv" >>$file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -125,7 +125,6 @@ function __tailscale_requires_order_preservation
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# This function does two things:
|
# This function does two things:
|
||||||
# - Obtain the completions and store them in the global __tailscale_comp_results
|
# - Obtain the completions and store them in the global __tailscale_comp_results
|
||||||
# - Return false if file completion should be performed
|
# - Return false if file completion should be performed
|
||||||
@@ -242,7 +241,7 @@ end
|
|||||||
complete -c tailscale -e
|
complete -c tailscale -e
|
||||||
|
|
||||||
# this will get called after the two calls below and clear the $__tailscale_perform_completion_once_result global
|
# this will get called after the two calls below and clear the $__tailscale_perform_completion_once_result global
|
||||||
complete -c tailscale -n '__tailscale_clear_perform_completion_once_result'
|
complete -c tailscale -n __tailscale_clear_perform_completion_once_result
|
||||||
# The call to __tailscale_prepare_completions will setup __tailscale_comp_results
|
# The call to __tailscale_prepare_completions will setup __tailscale_comp_results
|
||||||
# which provides the program's completion choices.
|
# which provides the program's completion choices.
|
||||||
# If this doesn't require order preservation, we don't use the -k flag
|
# If this doesn't require order preservation, we don't use the -k flag
|
||||||
|
|||||||
@@ -80,69 +80,68 @@ set -g __fish_config_op_registry_keys \
|
|||||||
"zoxide:"
|
"zoxide:"
|
||||||
|
|
||||||
set -g __fish_config_op_registry_values \
|
set -g __fish_config_op_registry_values \
|
||||||
"autoexec/venv" \
|
autoexec/venv \
|
||||||
"logging/terminal-capture" \
|
logging/terminal-capture \
|
||||||
"autoexec/sync" \
|
autoexec/sync \
|
||||||
"logging/multiplexer-capture" \
|
logging/multiplexer-capture \
|
||||||
"integrations/terminal-abbrs" \
|
integrations/terminal-abbrs \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"aliases/dev-tools" \
|
aliases/dev-tools \
|
||||||
"autoexec/sync" \
|
autoexec/sync \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"aliases/shell-tools" \
|
aliases/shell-tools \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"aliases/dev-tools" \
|
aliases/dev-tools \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"overrides/environment" \
|
overrides/environment \
|
||||||
"overrides/environment" \
|
overrides/environment \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"greeting/greeting-message" \
|
greeting/greeting-message \
|
||||||
"overrides/environment" \
|
overrides/environment \
|
||||||
"overrides/environment" \
|
overrides/environment \
|
||||||
"overrides/privacy" \
|
overrides/privacy \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"integrations/notifications" \
|
integrations/notifications \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"aliases/dev-tools" \
|
aliases/dev-tools \
|
||||||
"autoexec/plugin-management" \
|
autoexec/plugin-management \
|
||||||
"greeting/first-run" \
|
greeting/first-run \
|
||||||
"overrides/prompt" \
|
overrides/prompt \
|
||||||
"aliases/shell-tools" \
|
aliases/shell-tools \
|
||||||
"integrations/history-logs" \
|
integrations/history-logs \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"logging/terminal-capture" \
|
logging/terminal-capture \
|
||||||
"logging/terminal-capture" \
|
logging/terminal-capture \
|
||||||
"aliases/shell-tools" \
|
aliases/shell-tools \
|
||||||
"integrations/history-logs" \
|
integrations/history-logs \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"autoexec/pkg-wrappers" \
|
autoexec/pkg-wrappers \
|
||||||
"logging/pkg-logs" \
|
logging/pkg-logs \
|
||||||
"aliases/network" \
|
aliases/network \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"aliases/search" \
|
aliases/search \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"logging/terminal-capture" \
|
logging/terminal-capture \
|
||||||
"integrations/window-mgmt" \
|
integrations/window-mgmt \
|
||||||
"integrations/window-mgmt" \
|
integrations/window-mgmt \
|
||||||
"aliases/network" \
|
aliases/network \
|
||||||
"overrides/prompt" \
|
overrides/prompt \
|
||||||
"integrations/window-mgmt" \
|
integrations/window-mgmt \
|
||||||
"overrides/prompt" \
|
overrides/prompt \
|
||||||
"logging/multiplexer-capture" \
|
logging/multiplexer-capture \
|
||||||
"aliases/monitor" \
|
aliases/monitor \
|
||||||
"aliases/filesystem" \
|
aliases/filesystem \
|
||||||
"overrides/key-bindings" \
|
overrides/key-bindings \
|
||||||
"overrides/environment" \
|
overrides/environment \
|
||||||
"integrations/pkg-upgrade" \
|
integrations/pkg-upgrade \
|
||||||
"autoexec/telemetry" \
|
autoexec/telemetry \
|
||||||
"integrations/notifications" \
|
integrations/notifications \
|
||||||
"autoexec/pkg-wrappers" \
|
autoexec/pkg-wrappers \
|
||||||
"logging/pkg-logs" \
|
logging/pkg-logs \
|
||||||
"aliases/network" \
|
aliases/network \
|
||||||
"aliases/filesystem"
|
aliases/filesystem
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ function expand_bang_all --description 'Execute expand_bang_all'
|
|||||||
__fish_config_op_enabled (status basename); or return 1
|
__fish_config_op_enabled (status basename); or return 1
|
||||||
|
|
||||||
set -l token $argv[1]
|
set -l token $argv[1]
|
||||||
if test -z "$token"; set token (commandline -t); end
|
if test -z "$token"
|
||||||
|
set token (commandline -t)
|
||||||
|
end
|
||||||
|
|
||||||
set -l tokens (string split -n " " -- $history[1])
|
set -l tokens (string split -n " " -- $history[1])
|
||||||
if test (count $tokens) -gt 1
|
if test (count $tokens) -gt 1
|
||||||
@@ -46,7 +48,9 @@ function expand_bang_minus_n --description 'Execute expand_bang_minus_n'
|
|||||||
__fish_config_op_enabled (status basename); or return 1
|
__fish_config_op_enabled (status basename); or return 1
|
||||||
|
|
||||||
set -l token $argv[1]
|
set -l token $argv[1]
|
||||||
if test -z "$token"; set token (commandline -t); end
|
if test -z "$token"
|
||||||
|
set token (commandline -t)
|
||||||
|
end
|
||||||
|
|
||||||
# Extract the number from the regex match
|
# Extract the number from the regex match
|
||||||
if string match -qr '!-(\d+)' -- "$token"
|
if string match -qr '!-(\d+)' -- "$token"
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ printf '%s\n' \
|
|||||||
"cmd_str=\"$_paru_real\"" \
|
"cmd_str=\"$_paru_real\"" \
|
||||||
'for arg in "$@"; do' \
|
'for arg in "$@"; do' \
|
||||||
' cmd_str+=" $(printf '"'"'%q'"'"' "$arg")"' \
|
' cmd_str+=" $(printf '"'"'%q'"'"' "$arg")"' \
|
||||||
'done' \
|
done \
|
||||||
'script -q -e -c "$cmd_str" "$log_file"' \
|
'script -q -e -c "$cmd_str" "$log_file"' \
|
||||||
'exit_code=$?' \
|
'exit_code=$?' \
|
||||||
'' \
|
'' \
|
||||||
@@ -70,19 +70,18 @@ printf '%s\n' \
|
|||||||
'cleaner="${XDG_CONFIG_HOME:-$HOME/.config}/fish/scripts/clean_progress_log.py"' \
|
'cleaner="${XDG_CONFIG_HOME:-$HOME/.config}/fish/scripts/clean_progress_log.py"' \
|
||||||
'if command -v python3 >/dev/null 2>&1 && [[ -f "$cleaner" ]]; then' \
|
'if command -v python3 >/dev/null 2>&1 && [[ -f "$cleaner" ]]; then' \
|
||||||
' python3 "$cleaner" < "$log_file" > "${log_file}.tmp" 2>/dev/null && mv "${log_file}.tmp" "$log_file" || rm -f "${log_file}.tmp"' \
|
' python3 "$cleaner" < "$log_file" > "${log_file}.tmp" 2>/dev/null && mv "${log_file}.tmp" "$log_file" || rm -f "${log_file}.tmp"' \
|
||||||
'else' \
|
else \
|
||||||
' sed -i "/^Script \(started\|done\) on /d" "$log_file" 2>/dev/null || true' \
|
' sed -i "/^Script \(started\|done\) on /d" "$log_file" 2>/dev/null || true' \
|
||||||
'fi' \
|
fi \
|
||||||
'' \
|
'' \
|
||||||
'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \
|
'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \
|
||||||
'mapfile -t logs < <(ls -1t "$log_dir"/paru_*.log 2>/dev/null)' \
|
'mapfile -t logs < <(ls -1t "$log_dir"/paru_*.log 2>/dev/null)' \
|
||||||
'excess=$(( ${#logs[@]} - max_files ))' \
|
'excess=$(( ${#logs[@]} - max_files ))' \
|
||||||
'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \
|
'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \
|
||||||
' rm -f "${logs[$i]}"' \
|
' rm -f "${logs[$i]}"' \
|
||||||
'done' \
|
done \
|
||||||
'' \
|
'' \
|
||||||
'exit $exit_code' \
|
'exit $exit_code' >$_paru_wrapper
|
||||||
> $_paru_wrapper
|
|
||||||
|
|
||||||
chmod +x $_paru_wrapper
|
chmod +x $_paru_wrapper
|
||||||
|
|
||||||
|
|||||||
+26
-25
@@ -20,37 +20,38 @@ __fish_config_op_enabled (status basename) wakatime-autoexec; or exit
|
|||||||
__fish_config_op_enabled (status basename) wakatime-hook; or exit
|
__fish_config_op_enabled (status basename) wakatime-hook; or exit
|
||||||
|
|
||||||
function __register_wakatime_fish_before_exec -e fish_postexec
|
function __register_wakatime_fish_before_exec -e fish_postexec
|
||||||
if set -q FISH_WAKATIME_DISABLED
|
if set -q FISH_WAKATIME_DISABLED
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l exec_command_str
|
set -l exec_command_str
|
||||||
|
|
||||||
set exec_command_str (string split -f1 ' ' "$argv")
|
set exec_command_str (string split -f1 ' ' "$argv")
|
||||||
|
|
||||||
if test "$exec_command_str" = 'exit'
|
if test "$exec_command_str" = exit
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l PLUGIN_NAME "ik11235/wakatime.fish"
|
set -l PLUGIN_NAME "ik11235/wakatime.fish"
|
||||||
set -l PLUGIN_VERSION "0.0.6"
|
set -l PLUGIN_VERSION "0.0.6"
|
||||||
|
|
||||||
set -l project
|
set -l project
|
||||||
set -l wakatime_path
|
set -l wakatime_path
|
||||||
|
|
||||||
if type -p wakatime 2>&1 > /dev/null
|
if type -p wakatime 2>&1 >/dev/null
|
||||||
set wakatime_path (type -p wakatime)
|
set wakatime_path (type -p wakatime)
|
||||||
else if type -p ~/.wakatime/wakatime-cli 2>&1 > /dev/null
|
else if type -p ~/.wakatime/wakatime-cli 2>&1 >/dev/null
|
||||||
set wakatime_path (type -p ~/.wakatime/wakatime-cli)
|
set wakatime_path (type -p ~/.wakatime/wakatime-cli)
|
||||||
else
|
else
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if git rev-parse --is-inside-work-tree &> /dev/null
|
if git rev-parse --is-inside-work-tree &>/dev/null
|
||||||
set project (basename (git rev-parse --show-toplevel))
|
set project (basename (git rev-parse --show-toplevel))
|
||||||
else
|
else
|
||||||
set project "Terminal"
|
set project Terminal
|
||||||
end
|
end
|
||||||
|
|
||||||
$wakatime_path --write --plugin "$PLUGIN_NAME/$PLUGIN_VERSION" --entity-type app --project "$project" --entity "$exec_command_str" &> /dev/null&; disown
|
$wakatime_path --write --plugin "$PLUGIN_NAME/$PLUGIN_VERSION" --entity-type app --project "$project" --entity "$exec_command_str" &>/dev/null &
|
||||||
|
disown
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ printf '%s\n' \
|
|||||||
"cmd_str=\"$_yay_real\"" \
|
"cmd_str=\"$_yay_real\"" \
|
||||||
'for arg in "$@"; do' \
|
'for arg in "$@"; do' \
|
||||||
' cmd_str+=" $(printf '"'"'%q'"'"' "$arg")"' \
|
' cmd_str+=" $(printf '"'"'%q'"'"' "$arg")"' \
|
||||||
'done' \
|
done \
|
||||||
'script -q -e -c "$cmd_str" "$log_file"' \
|
'script -q -e -c "$cmd_str" "$log_file"' \
|
||||||
'exit_code=$?' \
|
'exit_code=$?' \
|
||||||
'' \
|
'' \
|
||||||
@@ -70,19 +70,18 @@ printf '%s\n' \
|
|||||||
'cleaner="${XDG_CONFIG_HOME:-$HOME/.config}/fish/scripts/clean_progress_log.py"' \
|
'cleaner="${XDG_CONFIG_HOME:-$HOME/.config}/fish/scripts/clean_progress_log.py"' \
|
||||||
'if command -v python3 >/dev/null 2>&1 && [[ -f "$cleaner" ]]; then' \
|
'if command -v python3 >/dev/null 2>&1 && [[ -f "$cleaner" ]]; then' \
|
||||||
' python3 "$cleaner" < "$log_file" > "${log_file}.tmp" 2>/dev/null && mv "${log_file}.tmp" "$log_file" || rm -f "${log_file}.tmp"' \
|
' python3 "$cleaner" < "$log_file" > "${log_file}.tmp" 2>/dev/null && mv "${log_file}.tmp" "$log_file" || rm -f "${log_file}.tmp"' \
|
||||||
'else' \
|
else \
|
||||||
' sed -i "/^Script \(started\|done\) on /d" "$log_file" 2>/dev/null || true' \
|
' sed -i "/^Script \(started\|done\) on /d" "$log_file" 2>/dev/null || true' \
|
||||||
'fi' \
|
fi \
|
||||||
'' \
|
'' \
|
||||||
'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \
|
'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \
|
||||||
'mapfile -t logs < <(ls -1t "$log_dir"/yay_*.log 2>/dev/null)' \
|
'mapfile -t logs < <(ls -1t "$log_dir"/yay_*.log 2>/dev/null)' \
|
||||||
'excess=$(( ${#logs[@]} - max_files ))' \
|
'excess=$(( ${#logs[@]} - max_files ))' \
|
||||||
'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \
|
'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \
|
||||||
' rm -f "${logs[$i]}"' \
|
' rm -f "${logs[$i]}"' \
|
||||||
'done' \
|
done \
|
||||||
'' \
|
'' \
|
||||||
'exit $exit_code' \
|
'exit $exit_code' >$_yay_wrapper
|
||||||
> $_yay_wrapper
|
|
||||||
|
|
||||||
chmod +x $_yay_wrapper
|
chmod +x $_yay_wrapper
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -253,7 +253,7 @@ if status is-interactive
|
|||||||
#
|
#
|
||||||
# Resolve user-dots path. Customize via: set -U __fish_user_dots_path /your/path
|
# Resolve user-dots path. Customize via: set -U __fish_user_dots_path /your/path
|
||||||
set -q __fish_user_dots_path
|
set -q __fish_user_dots_path
|
||||||
or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish"
|
or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish"
|
||||||
# ────────────────────── user-dots convenience symlink ───────────────────
|
# ────────────────────── user-dots convenience symlink ───────────────────
|
||||||
# Keep $__fish_config_dir/user-dots tracking the resolved path so it can be
|
# Keep $__fish_config_dir/user-dots tracking the resolved path so it can be
|
||||||
# browsed from the fish config dir. Git-ignored. Controlled by the
|
# browsed from the fish config dir. Git-ignored. Controlled by the
|
||||||
|
|||||||
@@ -99,10 +99,16 @@ def render(registry: dict[str, list[str]]) -> str:
|
|||||||
lines += [f" {k} \\" for k in quoted_keys[:-1]] + [f" {quoted_keys[-1]}"]
|
lines += [f" {k} \\" for k in quoted_keys[:-1]] + [f" {quoted_keys[-1]}"]
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
values = ['"' + " ".join(registry[k]) + '"' for k in keys]
|
values = [
|
||||||
|
(
|
||||||
|
'"' + " ".join(registry[k]) + '"'
|
||||||
|
if any(c in " ".join(registry[k]) for c in ' \t*?[]"\'\\$')
|
||||||
|
else " ".join(registry[k])
|
||||||
|
)
|
||||||
|
for k in keys
|
||||||
|
]
|
||||||
lines.append("set -g __fish_config_op_registry_values \\")
|
lines.append("set -g __fish_config_op_registry_values \\")
|
||||||
lines += [f" {v} \\" for v in values[:-1]] + [f" {values[-1]}"]
|
lines += [f" {v} \\" for v in values[:-1]] + [f" {values[-1]}"]
|
||||||
lines.append("")
|
|
||||||
return "\n".join(lines) + "\n"
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ function __auto_source_fallback_venv --on-variable PWD
|
|||||||
end
|
end
|
||||||
|
|
||||||
# 2. If we are already in a venv, check if we've left its tree
|
# 2. If we are already in a venv, check if we've left its tree
|
||||||
if set -q VIRTUAL_ENV
|
if set -q VIRTUAL_ENV
|
||||||
# Check if the current PWD is still within the directory that owns the venv
|
# Check if the current PWD is still within the directory that owns the venv
|
||||||
# (Assuming the venv is at the root of the project)
|
# (Assuming the venv is at the root of the project)
|
||||||
set -l venv_root (string replace -r '/.venv$' '' $VIRTUAL_ENV)
|
set -l venv_root (string replace -r '/.venv$' '' $VIRTUAL_ENV)
|
||||||
if not string match -q "$venv_root*" "$PWD"
|
if not string match -q "$venv_root*" "$PWD"
|
||||||
type -q deactivate; and deactivate
|
type -q deactivate; and deactivate
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# 3. Only source the venv if we aren't already in one
|
# 3. Only source the venv if we aren't already in one
|
||||||
if test -e ".venv/bin/activate.fish"
|
if test -e ".venv/bin/activate.fish"
|
||||||
source .venv/bin/activate.fish
|
source .venv/bin/activate.fish
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,8 +27,8 @@
|
|||||||
# __config_settings_apply __fish_config_op_greeting session DEFAULT
|
# __config_settings_apply __fish_config_op_greeting session DEFAULT
|
||||||
function __config_settings_apply
|
function __config_settings_apply
|
||||||
set -l varname $argv[1]
|
set -l varname $argv[1]
|
||||||
set -l scope $argv[2]
|
set -l scope $argv[2]
|
||||||
set -l value $argv[3]
|
set -l value $argv[3]
|
||||||
|
|
||||||
# stderr is suppressed because setting a value in one scope while the
|
# stderr is suppressed because setting a value in one scope while the
|
||||||
# other scope already holds the same variable makes interactive fish
|
# other scope already holds the same variable makes interactive fish
|
||||||
|
|||||||
@@ -27,8 +27,8 @@
|
|||||||
# __config_settings_set_value __fish_user_dots_path path '' # reset
|
# __config_settings_set_value __fish_user_dots_path path '' # reset
|
||||||
function __config_settings_set_value
|
function __config_settings_set_value
|
||||||
set -l varname $argv[1]
|
set -l varname $argv[1]
|
||||||
set -l type $argv[2]
|
set -l type $argv[2]
|
||||||
set -l value $argv[3]
|
set -l value $argv[3]
|
||||||
|
|
||||||
# stderr suppressed: editing a universal while a global of the same name
|
# stderr suppressed: editing a universal while a global of the same name
|
||||||
# shadows it makes interactive fish emit a shadow warning that would
|
# shadows it makes interactive fish emit a shadow warning that would
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ function __config_settings_subcats --description 'List the sub-categories for on
|
|||||||
case __fish_config_op_aliases
|
case __fish_config_op_aliases
|
||||||
printf '%s\t%s\t%s\n' \
|
printf '%s\t%s\t%s\n' \
|
||||||
filesystem Filesystem "ls, cat, cd, du, mkdir, rm, mv, zoxide" \
|
filesystem Filesystem "ls, cat, cd, du, mkdir, rm, mv, zoxide" \
|
||||||
search Search "rg" \
|
search Search rg \
|
||||||
network Network "ping, ssh, yt-dlp" \
|
network Network "ping, ssh, yt-dlp" \
|
||||||
monitor Monitor "top" \
|
monitor Monitor top \
|
||||||
shell-tools Shell-tools "bash, less, help" \
|
shell-tools Shell-tools "bash, less, help" \
|
||||||
dev-tools Dev-tools "claude, edit, agy"
|
dev-tools Dev-tools "claude, edit, agy"
|
||||||
case __fish_config_op_autoexec
|
case __fish_config_op_autoexec
|
||||||
@@ -52,7 +52,7 @@ function __config_settings_subcats --description 'List the sub-categories for on
|
|||||||
window-mgmt Window-mgmt "spwin, tab, split" \
|
window-mgmt Window-mgmt "spwin, tab, split" \
|
||||||
notifications Notifications "done, WakaTime hook" \
|
notifications Notifications "done, WakaTime hook" \
|
||||||
history-logs History-logs "hist, logs" \
|
history-logs History-logs "hist, logs" \
|
||||||
pkg-upgrade Pkg-upgrade "upgrade"
|
pkg-upgrade Pkg-upgrade upgrade
|
||||||
case __fish_config_op_logging
|
case __fish_config_op_logging
|
||||||
printf '%s\t%s\t%s\n' \
|
printf '%s\t%s\t%s\n' \
|
||||||
terminal-capture Term-capture "Kitty watcher, smart_exit scrollback" \
|
terminal-capture Term-capture "Kitty watcher, smart_exit scrollback" \
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
function __fish_user_dots_link --description 'Manage the user-dots convenience symlink'
|
function __fish_user_dots_link --description 'Manage the user-dots convenience symlink'
|
||||||
set -l link "$__fish_config_dir/user-dots"
|
set -l link "$__fish_config_dir/user-dots"
|
||||||
set -q __fish_user_dots_path
|
set -q __fish_user_dots_path
|
||||||
or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish"
|
or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish"
|
||||||
|
|
||||||
# Explicit opt-out: remove our symlink (never a real file/dir) and stop.
|
# Explicit opt-out: remove our symlink (never a real file/dir) and stop.
|
||||||
__fish_variable_check __fish_user_dots_symlink
|
__fish_variable_check __fish_user_dots_symlink
|
||||||
@@ -49,7 +49,7 @@ function __fish_user_dots_link --description 'Manage the user-dots convenience s
|
|||||||
test -d "$__fish_user_dots_path"; or return 0
|
test -d "$__fish_user_dots_path"; or return 0
|
||||||
if test -L "$link"
|
if test -L "$link"
|
||||||
test (readlink "$link") != "$__fish_user_dots_path"
|
test (readlink "$link") != "$__fish_user_dots_path"
|
||||||
and ln -sfn "$__fish_user_dots_path" "$link"
|
and ln -sfn "$__fish_user_dots_path" "$link"
|
||||||
else if not test -e "$link"
|
else if not test -e "$link"
|
||||||
ln -s "$__fish_user_dots_path" "$link"
|
ln -s "$__fish_user_dots_path" "$link"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ function __substitute_typo
|
|||||||
set -l expanded (string replace -a -- "$old" "$new" "$last_cmd")
|
set -l expanded (string replace -a -- "$old" "$new" "$last_cmd")
|
||||||
commandline -r "$expanded"
|
commandline -r "$expanded"
|
||||||
# No need to move cursor, it's a whole new line
|
# No need to move cursor, it's a whole new line
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# If it's just a normal caret (not part of a pattern), just insert it
|
# If it's just a normal caret (not part of a pattern), just insert it
|
||||||
commandline -i '^'
|
commandline -i '^'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ function _agents_init_ensure_gitignore
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l root $argv[1]
|
set -l root $argv[1]
|
||||||
set -l label $argv[2]
|
set -l label $argv[2]
|
||||||
set -l patterns $argv[3..]
|
set -l patterns $argv[3..]
|
||||||
set -l gitignore "$root/.gitignore"
|
set -l gitignore "$root/.gitignore"
|
||||||
|
|
||||||
|
|||||||
@@ -75,14 +75,14 @@ function _fish_deps_install
|
|||||||
set -l _major (fish --version 2>&1 | string match -r 'version (\d+)')[2]
|
set -l _major (fish --version 2>&1 | string match -r 'version (\d+)')[2]
|
||||||
if test -n "$_major"; and test "$_major" -lt 4
|
if test -n "$_major"; and test "$_major" -lt 4
|
||||||
set needs_install 1
|
set needs_install 1
|
||||||
set upgrade_label "Upgrade"
|
set upgrade_label Upgrade
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if test $needs_install -eq 1
|
if test $needs_install -eq 1
|
||||||
set -l cargo_crate $_fdc_cargo[$i]
|
set -l cargo_crate $_fdc_cargo[$i]
|
||||||
set -l pm_pkg $_fdc_pm[$i]
|
set -l pm_pkg $_fdc_pm[$i]
|
||||||
set -l special $_fdc_special[$i]
|
set -l special $_fdc_special[$i]
|
||||||
|
|
||||||
# Build list of available install methods
|
# Build list of available install methods
|
||||||
set -l methods
|
set -l methods
|
||||||
@@ -193,7 +193,9 @@ function _fish_deps_install
|
|||||||
echo " Available methods:"
|
echo " Available methods:"
|
||||||
set -l m 1
|
set -l m 1
|
||||||
for lbl in $method_labels
|
for lbl in $method_labels
|
||||||
set_color brblack; echo -n " $m) "; set_color normal
|
set_color brblack
|
||||||
|
echo -n " $m) "
|
||||||
|
set_color normal
|
||||||
echo $lbl
|
echo $lbl
|
||||||
set m (math $m + 1)
|
set m (math $m + 1)
|
||||||
end
|
end
|
||||||
@@ -202,7 +204,9 @@ function _fish_deps_install
|
|||||||
set chosen_method $methods[$_choice]
|
set chosen_method $methods[$_choice]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
set_color brblack; echo " "(string lower $upgrade_label)"ing via $method_labels[1]"; set_color normal
|
set_color brblack
|
||||||
|
echo " "(string lower $upgrade_label)"ing via $method_labels[1]"
|
||||||
|
set_color normal
|
||||||
end
|
end
|
||||||
|
|
||||||
# Execute chosen method
|
# Execute chosen method
|
||||||
@@ -329,14 +333,18 @@ function _fish_deps_install
|
|||||||
|
|
||||||
if test $status -eq 0
|
if test $status -eq 0
|
||||||
set installed_any 1
|
set installed_any 1
|
||||||
set_color green; echo " $bin "(string lower $upgrade_label)"ed."; set_color normal
|
set_color green
|
||||||
|
echo " $bin "(string lower $upgrade_label)"ed."
|
||||||
|
set_color normal
|
||||||
if test "$bin" = fish
|
if test "$bin" = fish
|
||||||
set_color yellow
|
set_color yellow
|
||||||
echo " Fish upgraded — restart your shell to use the new version."
|
echo " Fish upgraded — restart your shell to use the new version."
|
||||||
set_color normal
|
set_color normal
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
set_color red; echo " $bin "(string lower $upgrade_label)" failed."; set_color normal
|
set_color red
|
||||||
|
echo " $bin "(string lower $upgrade_label)" failed."
|
||||||
|
set_color normal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
set i (math $i + 1)
|
set i (math $i + 1)
|
||||||
|
|||||||
@@ -23,35 +23,57 @@ function _fish_deps_status
|
|||||||
set -l _major (fish --version 2>&1 | string match -r 'version (\d+)')[2]
|
set -l _major (fish --version 2>&1 | string match -r 'version (\d+)')[2]
|
||||||
if test -n "$_major"; and test "$_major" -lt 4
|
if test -n "$_major"; and test "$_major" -lt 4
|
||||||
set -l _ver (fish --version 2>&1 | string replace 'fish, ' '')
|
set -l _ver (fish --version 2>&1 | string replace 'fish, ' '')
|
||||||
set_color yellow; echo -n " ⚠ "; set_color normal
|
set_color yellow
|
||||||
|
echo -n " ⚠ "
|
||||||
|
set_color normal
|
||||||
echo -n "$bin "
|
echo -n "$bin "
|
||||||
set_color brblack; echo "($_ver — upgrade to 4.0+ required)"; set_color normal
|
set_color brblack
|
||||||
|
echo "($_ver — upgrade to 4.0+ required)"
|
||||||
|
set_color normal
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
set_color green; echo -n " ✓ "; set_color normal
|
set_color green
|
||||||
|
echo -n " ✓ "
|
||||||
|
set_color normal
|
||||||
echo -n "$bin "
|
echo -n "$bin "
|
||||||
set_color brblack; echo "(Found at "(__fish_real_command $bin)")"; set_color normal
|
set_color brblack
|
||||||
|
echo "(Found at "(__fish_real_command $bin)")"
|
||||||
|
set_color normal
|
||||||
else if test "$tier" = req
|
else if test "$tier" = req
|
||||||
set_color red; echo -n " ✗ "; set_color normal
|
set_color red
|
||||||
|
echo -n " ✗ "
|
||||||
|
set_color normal
|
||||||
echo -n "$bin "
|
echo -n "$bin "
|
||||||
set_color brblack; echo "(Not installed)"; set_color normal
|
set_color brblack
|
||||||
|
echo "(Not installed)"
|
||||||
|
set_color normal
|
||||||
else if test "$tier" = rec
|
else if test "$tier" = rec
|
||||||
set_color yellow; echo -n " ⚠ "; set_color normal
|
set_color yellow
|
||||||
|
echo -n " ⚠ "
|
||||||
|
set_color normal
|
||||||
echo -n "$bin "
|
echo -n "$bin "
|
||||||
set_color brblack; echo "(Not installed)"; set_color normal
|
set_color brblack
|
||||||
|
echo "(Not installed)"
|
||||||
|
set_color normal
|
||||||
else
|
else
|
||||||
# opt / term / int: absence is expected and not alarming
|
# opt / term / int: absence is expected and not alarming
|
||||||
set_color brblack; echo -n " – "; set_color normal
|
set_color brblack
|
||||||
|
echo -n " – "
|
||||||
|
set_color normal
|
||||||
echo -n "$bin "
|
echo -n "$bin "
|
||||||
set_color brblack; echo "(Not installed)"; set_color normal
|
set_color brblack
|
||||||
|
echo "(Not installed)"
|
||||||
|
set_color normal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for tier_label in "Required Dependencies:req" "Recommended Dependencies:rec" "Optional Dependencies:opt" "Terminal Emulators:term" "Integrations:int"
|
for tier_label in "Required Dependencies:req" "Recommended Dependencies:rec" "Optional Dependencies:opt" "Terminal Emulators:term" "Integrations:int"
|
||||||
set -l label (string split : $tier_label)[1]
|
set -l label (string split : $tier_label)[1]
|
||||||
set -l tier (string split : $tier_label)[2]
|
set -l tier (string split : $tier_label)[2]
|
||||||
set_color cyan; echo $label; set_color normal
|
set_color cyan
|
||||||
|
echo $label
|
||||||
|
set_color normal
|
||||||
set -l i 1
|
set -l i 1
|
||||||
for bin in $_fdc_bins
|
for bin in $_fdc_bins
|
||||||
if test "$_fdc_tiers[$i]" = $tier
|
if test "$_fdc_tiers[$i]" = $tier
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ function _fish_deps_update
|
|||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l cargo_crate $_fdc_cargo[$i]
|
set -l cargo_crate $_fdc_cargo[$i]
|
||||||
set -l pm_pkg $_fdc_pm[$i]
|
set -l pm_pkg $_fdc_pm[$i]
|
||||||
set -l special $_fdc_special[$i]
|
set -l special $_fdc_special[$i]
|
||||||
|
|
||||||
# yay: update via paru if available, else system PM
|
# yay: update via paru if available, else system PM
|
||||||
if test "$special" = yay-build
|
if test "$special" = yay-build
|
||||||
@@ -101,10 +101,14 @@ function _fish_deps_update
|
|||||||
echo "Updating $bin..."
|
echo "Updating $bin..."
|
||||||
set -l _arch (uname -m)
|
set -l _arch (uname -m)
|
||||||
switch $_arch
|
switch $_arch
|
||||||
case x86_64; set _arch amd64
|
case x86_64
|
||||||
case aarch64 arm64; set _arch arm64
|
set _arch amd64
|
||||||
case armv7l; set _arch arm
|
case aarch64 arm64
|
||||||
case '*'; set _arch amd64
|
set _arch arm64
|
||||||
|
case armv7l
|
||||||
|
set _arch arm
|
||||||
|
case '*'
|
||||||
|
set _arch amd64
|
||||||
end
|
end
|
||||||
set -l _zip "wakatime-cli-linux-$_arch.zip"
|
set -l _zip "wakatime-cli-linux-$_arch.zip"
|
||||||
set -l _bin_src "wakatime-cli-linux-$_arch"
|
set -l _bin_src "wakatime-cli-linux-$_arch"
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ function _fish_mkdir_p --description 'mkdir -p with configurable verbose output'
|
|||||||
while not test -d $cursor
|
while not test -d $cursor
|
||||||
set -p to_create $cursor
|
set -p to_create $cursor
|
||||||
set -l up (dirname $cursor)
|
set -l up (dirname $cursor)
|
||||||
test "$up" = "$cursor"; and break # filesystem root guard
|
test "$up" = "$cursor"; and break # filesystem root guard
|
||||||
set cursor $up
|
set cursor $up
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ function _puffer_fish_expand_bang
|
|||||||
commandline --insert '!'
|
commandline --insert '!'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ function _puffer_fish_expand_star
|
|||||||
else if string match --quiet -- '!' "$(commandline --current-token)"
|
else if string match --quiet -- '!' "$(commandline --current-token)"
|
||||||
set -l prev_cmd $history[1]
|
set -l prev_cmd $history[1]
|
||||||
set -l prev_args (string split ' ' $prev_cmd)
|
set -l prev_args (string split ' ' $prev_cmd)
|
||||||
set -e prev_args[1] # remove command name
|
set -e prev_args[1] # remove command name
|
||||||
set -l arg_str (string join ' ' $prev_args)
|
set -l arg_str (string join ' ' $prev_args)
|
||||||
# replace !* with all arguments
|
# replace !* with all arguments
|
||||||
commandline --current-token ''
|
commandline --current-token ''
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ function _smart_execute --description 'Execute different functions based on the
|
|||||||
# If it ends in =, run qalc; fall back to normal execute if qalc is absent
|
# If it ends in =, run qalc; fall back to normal execute if qalc is absent
|
||||||
_qalc_eval; or commandline -f execute
|
_qalc_eval; or commandline -f execute
|
||||||
|
|
||||||
# case 'g *'
|
# case 'g *'
|
||||||
# # EXAMPLE FUTURE EXTENSION
|
# # EXAMPLE FUTURE EXTENSION
|
||||||
# _some_git_helper
|
# _some_git_helper
|
||||||
|
|
||||||
case '*'
|
case '*'
|
||||||
# Default: execute the command line as-is
|
# Default: execute the command line as-is
|
||||||
|
|||||||
@@ -893,8 +893,7 @@ function agents-vault --description 'track curated agent memory in a host-scoped
|
|||||||
command cp -rn "$stash/." "$entry/" 2>/dev/null
|
command cp -rn "$stash/." "$entry/" 2>/dev/null
|
||||||
rm -rf "$stash"
|
rm -rf "$stash"
|
||||||
end
|
end
|
||||||
printf 'renamed: %s → %s (%s)\n' "$prev_slug" "$slug" (date -I) \
|
printf 'renamed: %s → %s (%s)\n' "$prev_slug" "$slug" (date -I) >>"$entry/origin"
|
||||||
>>"$entry/origin"
|
|
||||||
# Only a link is dropped here, and only so the relink below has
|
# Only a link is dropped here, and only so the relink below has
|
||||||
# somewhere to put the new one. Usually $live is exactly that: a
|
# somewhere to put the new one. Usually $live is exactly that: a
|
||||||
# symlink at the old entry, now dangling. But the migration is
|
# symlink at the old entry, now dangling. But the migration is
|
||||||
@@ -1078,8 +1077,7 @@ function agents-vault --description 'track curated agent memory in a host-scoped
|
|||||||
end
|
end
|
||||||
set -l reached 1
|
set -l reached 1
|
||||||
if git -C "$vault" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1
|
if git -C "$vault" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1
|
||||||
if not $gitnet git -C "$vault" pull --rebase --autostash -q \
|
if not $gitnet git -C "$vault" pull --rebase --autostash -q >/dev/null 2>/dev/null
|
||||||
>/dev/null 2>/dev/null
|
|
||||||
# Two unrelated failures land here and reporting them as
|
# Two unrelated failures land here and reporting them as
|
||||||
# one sends the user hunting for a conflict that never
|
# one sends the user hunting for a conflict that never
|
||||||
# existed. A rebase that genuinely started and stopped
|
# existed. A rebase that genuinely started and stopped
|
||||||
|
|||||||
+4
-4
@@ -52,10 +52,10 @@ function agy --wraps=agy --description 'agy wrapper: auto-initializes AGENTS/ su
|
|||||||
agents-vault --quiet
|
agents-vault --quiet
|
||||||
|
|
||||||
for i in (seq (count $argv))
|
for i in (seq (count $argv))
|
||||||
if test "$argv[$i]" = "-r"
|
if test "$argv[$i]" = -r
|
||||||
set argv[$i] "-c"
|
set argv[$i] -c
|
||||||
else if test "$argv[$i]" = "--resume"
|
else if test "$argv[$i]" = --resume
|
||||||
set argv[$i] "--continue"
|
set argv[$i] --continue
|
||||||
else if string match -q -- "--resume=*" "$argv[$i]"
|
else if string match -q -- "--resume=*" "$argv[$i]"
|
||||||
set argv[$i] (string replace -- "--resume=" "--continue=" "$argv[$i]")
|
set argv[$i] (string replace -- "--resume=" "--continue=" "$argv[$i]")
|
||||||
end
|
end
|
||||||
|
|||||||
+15
-6
@@ -25,11 +25,20 @@
|
|||||||
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
||||||
__fish_help_header (status current-function) $argv; and return 0
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not set -q argv[1]; echo "Need repo owner/name"; return 1; end
|
if not set -q argv[1]
|
||||||
if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end
|
echo "Need repo owner/name"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if not set -q GITEA_TOKEN
|
||||||
|
echo "\$GITEA_TOKEN not set"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
set -l REPO $argv[1]
|
set -l REPO $argv[1]
|
||||||
if not set -q GITEA_URL; echo "\$GITEA_URL not set"; return 1; end
|
if not set -q GITEA_URL
|
||||||
|
echo "\$GITEA_URL not set"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
set -l IMPORT_COUNT 0
|
set -l IMPORT_COUNT 0
|
||||||
|
|
||||||
echo (set_color blue)"📡 Checking Gitea: $REPO..."(set_color normal)
|
echo (set_color blue)"📡 Checking Gitea: $REPO..."(set_color normal)
|
||||||
@@ -61,9 +70,9 @@ function bd-pull --description 'Pull new Gitea issues into local Beads and link
|
|||||||
# This prevents the Gitea Action from creating a duplicate
|
# This prevents the Gitea Action from creating a duplicate
|
||||||
set -l new_title "[$bid] $title"
|
set -l new_title "[$bid] $title"
|
||||||
curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" \
|
curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"title\":\"$new_title\"}" \
|
-d "{\"title\":\"$new_title\"}" \
|
||||||
"$GITEA_URL/api/v1/repos/$REPO/issues/$number" > /dev/null
|
"$GITEA_URL/api/v1/repos/$REPO/issues/$number" >/dev/null
|
||||||
|
|
||||||
set IMPORT_COUNT (math $IMPORT_COUNT + 1)
|
set IMPORT_COUNT (math $IMPORT_COUNT + 1)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -33,6 +33,6 @@ function branch --description 'Switch to or create a git branch'
|
|||||||
git checkout $argv
|
git checkout $argv
|
||||||
else
|
else
|
||||||
# If it doesn't exist, create it
|
# If it doesn't exist, create it
|
||||||
git checkout -b $argv
|
git checkout -b $argv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ function cleanup --description 'Log orphans to ~/.removed_orphans and remove the
|
|||||||
set -l orphans (pacman -Qtdq)
|
set -l orphans (pacman -Qtdq)
|
||||||
if test -n "$orphans"
|
if test -n "$orphans"
|
||||||
echo "📝 Logging orphans to ~/.removed_orphans..."
|
echo "📝 Logging orphans to ~/.removed_orphans..."
|
||||||
echo "--- Removed on $(date) ---" >> ~/.removed_orphans
|
echo "--- Removed on $(date) ---" >>~/.removed_orphans
|
||||||
pacman -Qi $orphans | grep -E '^(Name|Version)' >> ~/.removed_orphans
|
pacman -Qi $orphans | grep -E '^(Name|Version)' >>~/.removed_orphans
|
||||||
|
|
||||||
echo "🧹 Removing orphans..."
|
echo "🧹 Removing orphans..."
|
||||||
sudo pacman -Rns $orphans
|
sudo pacman -Rns $orphans
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function config-update --description 'Pull latest fish config from upstream'
|
|||||||
end
|
end
|
||||||
|
|
||||||
# ── compare local HEAD to upstream ───────────────────────────
|
# ── compare local HEAD to upstream ───────────────────────────
|
||||||
set -l local_sha (git -C "$CONFIG_DIR" rev-parse HEAD 2>/dev/null)
|
set -l local_sha (git -C "$CONFIG_DIR" rev-parse HEAD 2>/dev/null)
|
||||||
set -l remote_sha (git -C "$CONFIG_DIR" rev-parse _config_update/main 2>/dev/null)
|
set -l remote_sha (git -C "$CONFIG_DIR" rev-parse _config_update/main 2>/dev/null)
|
||||||
|
|
||||||
if test "$local_sha" = "$remote_sha"
|
if test "$local_sha" = "$remote_sha"
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ function fish_prompt
|
|||||||
set -l last_status $status
|
set -l last_status $status
|
||||||
|
|
||||||
# Catppuccin Mocha hex palette
|
# Catppuccin Mocha hex palette
|
||||||
set -l c_green '#a6e3a1'
|
set -l c_green '#a6e3a1'
|
||||||
set -l c_red '#f38ba8'
|
set -l c_red '#f38ba8'
|
||||||
set -l c_yellow '#f9e2af'
|
set -l c_yellow '#f9e2af'
|
||||||
set -l c_text '#cdd6f4'
|
set -l c_text '#cdd6f4'
|
||||||
set -l c_blue '#89b4fa'
|
set -l c_blue '#89b4fa'
|
||||||
set -l c_teal '#94e2d5'
|
set -l c_teal '#94e2d5'
|
||||||
set -l c_pink '#f5c2e7'
|
set -l c_pink '#f5c2e7'
|
||||||
set -l c_dim '#6c7086'
|
set -l c_dim '#6c7086'
|
||||||
set -l c_mauve '#cba6f7'
|
set -l c_mauve '#cba6f7'
|
||||||
|
|
||||||
# Line/connector color tracks last exit status; brackets stay bold green
|
# Line/connector color tracks last exit status; brackets stay bold green
|
||||||
set -l retc $c_green
|
set -l retc $c_green
|
||||||
@@ -129,7 +129,7 @@ function fish_prompt
|
|||||||
set_color normal
|
set_color normal
|
||||||
end
|
end
|
||||||
|
|
||||||
echo # newline
|
echo # newline
|
||||||
|
|
||||||
# Background jobs (one per line, dim)
|
# Background jobs (one per line, dim)
|
||||||
for job in (jobs -c)
|
for job in (jobs -c)
|
||||||
|
|||||||
+5
-6
@@ -135,11 +135,11 @@ function logs --description 'Browse terminal log files interactively with fzf'
|
|||||||
set -l togglescript (mktemp)
|
set -l togglescript (mktemp)
|
||||||
set -l deletescript (mktemp)
|
set -l deletescript (mktemp)
|
||||||
set -l helpflag "$tmpfile.help"
|
set -l helpflag "$tmpfile.help"
|
||||||
printf '%s\n' $fzf_lines > $tmpfile
|
printf '%s\n' $fzf_lines >$tmpfile
|
||||||
printf 'Terminal Logs — Key Bindings\n\n Enter View selected log in pager\n Ctrl-E Edit selected log in editor\n Ctrl-D Delete selected log\n Ctrl-C Quit\n ? Toggle this help\n\nFiltering:\n Type to fuzzy-filter by date or category\n Use -c flag to limit: scrollback, paru, yay' > $helpfile
|
printf 'Terminal Logs — Key Bindings\n\n Enter View selected log in pager\n Ctrl-E Edit selected log in editor\n Ctrl-D Delete selected log\n Ctrl-C Quit\n ? Toggle this help\n\nFiltering:\n Type to fuzzy-filter by date or category\n Use -c flag to limit: scrollback, paru, yay' >$helpfile
|
||||||
# Help toggle script — avoids nested parens inside transform()
|
# Help toggle script — avoids nested parens inside transform()
|
||||||
printf '#!/bin/sh\nif test -f %s; then\n rm -f %s\n printf "change-preview(command cat {1})"\nelse\n touch %s\n printf "change-preview(command cat %s)"\nfi\n' \
|
printf '#!/bin/sh\nif test -f %s; then\n rm -f %s\n printf "change-preview(command cat {1})"\nelse\n touch %s\n printf "change-preview(command cat %s)"\nfi\n' \
|
||||||
$helpflag $helpflag $helpflag $helpfile > $togglescript
|
$helpflag $helpflag $helpflag $helpfile >$togglescript
|
||||||
chmod +x $togglescript
|
chmod +x $togglescript
|
||||||
# Delete confirmation script — avoids nested parens inside execute(), and ensures
|
# Delete confirmation script — avoids nested parens inside execute(), and ensures
|
||||||
# tmpfile is updated before +reload fires (execute is synchronous, execute-silent is not)
|
# tmpfile is updated before +reload fires (execute is synchronous, execute-silent is not)
|
||||||
@@ -151,8 +151,7 @@ function logs --description 'Browse terminal log files interactively with fzf'
|
|||||||
'case "$confirm" in [nN]) exit 0 ;; esac' \
|
'case "$confirm" in [nN]) exit 0 ;; esac' \
|
||||||
"fish -c \"rm \$FILE\"" \
|
"fish -c \"rm \$FILE\"" \
|
||||||
"grep -vF \"\$FILE\" $tmpfile > $tmpfile.new" \
|
"grep -vF \"\$FILE\" $tmpfile > $tmpfile.new" \
|
||||||
"mv $tmpfile.new $tmpfile" \
|
"mv $tmpfile.new $tmpfile" >$deletescript
|
||||||
> $deletescript
|
|
||||||
chmod +x $deletescript
|
chmod +x $deletescript
|
||||||
|
|
||||||
set -l selected (command cat $tmpfile | fzf \
|
set -l selected (command cat $tmpfile | fzf \
|
||||||
@@ -196,7 +195,7 @@ function logs --description 'Browse terminal log files interactively with fzf'
|
|||||||
# is displayed without ov's default slateblue background, preserving
|
# is displayed without ov's default slateblue background, preserving
|
||||||
# the original ANSI colors of the starship prompt.
|
# the original ANSI colors of the starship prompt.
|
||||||
set -l ov_cfg (mktemp --suffix .yaml)
|
set -l ov_cfg (mktemp --suffix .yaml)
|
||||||
printf 'Mode:\n scrollback:\n Style:\n SectionLine:\n Background: ""\n Foreground: ""\n' > $ov_cfg
|
printf 'Mode:\n scrollback:\n Style:\n SectionLine:\n Background: ""\n Foreground: ""\n' >$ov_cfg
|
||||||
command ov --config $ov_cfg --view-mode scrollback \
|
command ov --config $ov_cfg --view-mode scrollback \
|
||||||
--section-delimiter '\x1b\]133;A' --section-header --section-header-num 1 $file
|
--section-delimiter '\x1b\]133;A' --section-header --section-header-num 1 $file
|
||||||
rm -f $ov_cfg
|
rm -f $ov_cfg
|
||||||
|
|||||||
+64
-16
@@ -187,28 +187,52 @@ function pkg --description 'Install or remove packages via the system package ma
|
|||||||
switch $pm
|
switch $pm
|
||||||
case paru yay
|
case paru yay
|
||||||
$pm -S $to_install
|
$pm -S $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case pacman
|
case pacman
|
||||||
sudo pacman -S $to_install
|
sudo pacman -S $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case apt
|
case apt
|
||||||
sudo apt install -y $to_install
|
sudo apt install -y $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case dnf
|
case dnf
|
||||||
sudo dnf install -y $to_install
|
sudo dnf install -y $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case zypper
|
case zypper
|
||||||
sudo zypper install -y $to_install
|
sudo zypper install -y $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case brew
|
case brew
|
||||||
brew install $to_install
|
brew install $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case pkg
|
case pkg
|
||||||
sudo pkg install -y $to_install
|
sudo pkg install -y $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case yum
|
case yum
|
||||||
sudo yum install -y $to_install
|
sudo yum install -y $to_install
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -217,28 +241,52 @@ function pkg --description 'Install or remove packages via the system package ma
|
|||||||
switch $pm
|
switch $pm
|
||||||
case paru yay
|
case paru yay
|
||||||
$pm -Rns $to_remove
|
$pm -Rns $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case pacman
|
case pacman
|
||||||
sudo pacman -Rns $to_remove
|
sudo pacman -Rns $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case apt
|
case apt
|
||||||
sudo apt remove -y $to_remove
|
sudo apt remove -y $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case dnf
|
case dnf
|
||||||
sudo dnf remove -y $to_remove
|
sudo dnf remove -y $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case zypper
|
case zypper
|
||||||
sudo zypper remove -y $to_remove
|
sudo zypper remove -y $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case brew
|
case brew
|
||||||
brew uninstall $to_remove
|
brew uninstall $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case pkg
|
case pkg
|
||||||
sudo pkg delete -y $to_remove
|
sudo pkg delete -y $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
case yum
|
case yum
|
||||||
sudo yum remove -y $to_remove
|
sudo yum remove -y $to_remove
|
||||||
or begin; functions -e __pkg_is_installed; return $status; end
|
or begin
|
||||||
|
functions -e __pkg_is_installed
|
||||||
|
return $status
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ function rand_string --description 'Generate random, memorable strings from cura
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l sep "dash"
|
set -l sep dash
|
||||||
set -l casing "lower"
|
set -l casing lower
|
||||||
|
|
||||||
# Locate the words directory relative to this function
|
# Locate the words directory relative to this function
|
||||||
set -l words_dir ""
|
set -l words_dir ""
|
||||||
@@ -114,9 +114,9 @@ function rand_string --description 'Generate random, memorable strings from cura
|
|||||||
set -l actual_sep $sep
|
set -l actual_sep $sep
|
||||||
switch $sep
|
switch $sep
|
||||||
case dash
|
case dash
|
||||||
set actual_sep "-"
|
set actual_sep -
|
||||||
case underscore
|
case underscore
|
||||||
set actual_sep "_"
|
set actual_sep _
|
||||||
case dot
|
case dot
|
||||||
set actual_sep "."
|
set actual_sep "."
|
||||||
case none empty
|
case none empty
|
||||||
|
|||||||
@@ -17,6 +17,6 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# rawfish
|
# rawfish
|
||||||
function rawfish --wraps='env NO_TMUX=1 fish' --description 'alias rawfish=env NO_TMUX=1 fish'
|
function rawfish --wraps='env NO_TMUX=1 fish' --description 'alias rawfish=env NO_TMUX=1 fish'
|
||||||
env NO_TMUX=1 fish $argv
|
env NO_TMUX=1 fish $argv
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -90,12 +90,12 @@ function repo-open --description 'Open the origin remote of the current repo in
|
|||||||
set had_scheme 1
|
set had_scheme 1
|
||||||
end
|
end
|
||||||
set -l u (string replace -r '^[a-z0-9]+://' '' -- $remote) # strip scheme
|
set -l u (string replace -r '^[a-z0-9]+://' '' -- $remote) # strip scheme
|
||||||
set u (string replace -r '^[^@/]+@' '' -- $u) # strip user@
|
set u (string replace -r '^[^@/]+@' '' -- $u) # strip user@
|
||||||
set u (string replace -r '\.git$' '' -- $u) # strip .git
|
set u (string replace -r '\.git$' '' -- $u) # strip .git
|
||||||
if test $had_scheme -eq 0
|
if test $had_scheme -eq 0
|
||||||
set u (string replace ':' '/' -- $u) # scp: first colon → path sep
|
set u (string replace ':' '/' -- $u) # scp: first colon → path sep
|
||||||
else
|
else
|
||||||
set u (string replace -r ':[0-9]+/' '/' -- $u) # url: drop :port
|
set u (string replace -r ':[0-9]+/' '/' -- $u) # url: drop :port
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l parts (string split -m1 '/' -- $u)
|
set -l parts (string split -m1 '/' -- $u)
|
||||||
|
|||||||
+43
-43
@@ -24,53 +24,53 @@
|
|||||||
# sbver
|
# sbver
|
||||||
# sbver --brief
|
# sbver --brief
|
||||||
function sbver --description 'Verifies Secure Boot status of EFI binaries using sbctl'
|
function sbver --description 'Verifies Secure Boot status of EFI binaries using sbctl'
|
||||||
__fish_help_header (status current-function) $argv; and return 0
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not type -q sbctl
|
if not type -q sbctl
|
||||||
echo "Error: 'sbctl' is not installed."
|
echo "Error: 'sbctl' is not installed."
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
# ANSI color codes (Fish uses set_color for easier management)
|
# ANSI color codes (Fish uses set_color for easier management)
|
||||||
set RED (set_color red)
|
set RED (set_color red)
|
||||||
set GREEN (set_color green)
|
set GREEN (set_color green)
|
||||||
set NC (set_color normal)
|
set NC (set_color normal)
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
set brief_mode false
|
set brief_mode false
|
||||||
if test "$argv[1]" = "--brief"
|
if test "$argv[1]" = --brief
|
||||||
set brief_mode true
|
set brief_mode true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Counters
|
# Counters
|
||||||
set pass_count 0
|
set pass_count 0
|
||||||
set fail_count 0
|
set fail_count 0
|
||||||
|
|
||||||
# Run and process sbctl output
|
# Run and process sbctl output
|
||||||
# Fish doesn't use 'done < <()'; we pipe directly into the while loop
|
# Fish doesn't use 'done < <()'; we pipe directly into the while loop
|
||||||
sudo sbctl verify 2>&1 | grep -v -i 'invalid pe header' | while read -l line
|
sudo sbctl verify 2>&1 | grep -v -i 'invalid pe header' | while read -l line
|
||||||
if string match -q "*✓*" -- "$line"
|
if string match -q "*✓*" -- "$line"
|
||||||
set pass_count (math $pass_count + 1)
|
set pass_count (math $pass_count + 1)
|
||||||
if not $brief_mode
|
if not $brief_mode
|
||||||
echo -e "$GREEN$line$NC"
|
echo -e "$GREEN$line$NC"
|
||||||
end
|
end
|
||||||
else if string match -q "*✗*" -- "$line"
|
else if string match -q "*✗*" -- "$line"
|
||||||
set fail_count (math $fail_count + 1)
|
set fail_count (math $fail_count + 1)
|
||||||
if not $brief_mode
|
if not $brief_mode
|
||||||
echo -e "$RED$line$NC"
|
echo -e "$RED$line$NC"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if not $brief_mode
|
if not $brief_mode
|
||||||
echo "$line"
|
echo "$line"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
echo
|
echo
|
||||||
if test $fail_count -eq 0
|
if test $fail_count -eq 0
|
||||||
echo -e "$GREEN✅ All images are signed ($pass_count verified)$NC"
|
echo -e "$GREEN✅ All images are signed ($pass_count verified)$NC"
|
||||||
else
|
else
|
||||||
echo -e "$RED❌ Some images are not signed ($fail_count failed, $pass_count passed)$NC"
|
echo -e "$RED❌ Some images are not signed ($fail_count failed, $pass_count passed)$NC"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
# spark 1 1 2 5 14 42
|
# spark 1 1 2 5 14 42
|
||||||
# seq 64 | sort --random-sort | spark
|
# seq 64 | sort --random-sort | spark
|
||||||
# echo "3 7 2 9 1" | spark
|
# echo "3 7 2 9 1" | spark
|
||||||
function spark --description 'Sparklines'
|
function spark --description Sparklines
|
||||||
argparse --ignore-unknown --name=spark v/version h/help m/min= M/max= -- $argv || return
|
argparse --ignore-unknown --name=spark v/version h/help m/min= M/max= -- $argv || return
|
||||||
|
|
||||||
if set --query _flag_version[1]
|
if set --query _flag_version[1]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
# set -U -a sponge_filters sponge_filter_secrets
|
# set -U -a sponge_filters sponge_filter_secrets
|
||||||
function sponge_filter_secrets --argument-names command
|
function sponge_filter_secrets --argument-names command
|
||||||
# Find all exported variables with security-sensitive names
|
# Find all exported variables with security-sensitive names
|
||||||
set -l sensitive_vars (set --names --export | string match --regex -- \
|
set -l sensitive_vars (set --names --export | string match --entire --regex -- \
|
||||||
'(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)')
|
'(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)')
|
||||||
|
|
||||||
for var in $sensitive_vars
|
for var in $sensitive_vars
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function sudo-toggle --description 'Toggle sudo password requirement on/off'
|
|||||||
else
|
else
|
||||||
# 2. Toggle is currently ON (Security active). We want to BYPASS it.
|
# 2. Toggle is currently ON (Security active). We want to BYPASS it.
|
||||||
# We write a high-priority user-specific NOPASSWD rule.
|
# We write a high-priority user-specific NOPASSWD rule.
|
||||||
echo "$USER ALL=(ALL:ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/nofail-toggle > /dev/null
|
echo "$USER ALL=(ALL:ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/nofail-toggle >/dev/null
|
||||||
echo "🔓 Sudo security: DISABLED (Bypass active)"
|
echo "🔓 Sudo security: DISABLED (Bypass active)"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ function yt-dlp --description 'yt-dlp with embedding + SponsorBlock defaults'
|
|||||||
# --sponsorblock-remove all
|
# --sponsorblock-remove all
|
||||||
# Skip if the user set their own remove (bare or --opt=value form), or
|
# Skip if the user set their own remove (bare or --opt=value form), or
|
||||||
# disabled SponsorBlock entirely with --no-sponsorblock.
|
# disabled SponsorBlock entirely with --no-sponsorblock.
|
||||||
if not string match -q -- '--sponsorblock-remove' $argv
|
if not string match -q -- --sponsorblock-remove $argv
|
||||||
and not string match -q -- '--sponsorblock-remove=*' $argv
|
and not string match -q -- '--sponsorblock-remove=*' $argv
|
||||||
and not contains -- --no-sponsorblock $argv
|
and not contains -- --no-sponsorblock $argv
|
||||||
set -a extra --sponsorblock-remove all
|
set -a extra --sponsorblock-remove all
|
||||||
|
|||||||
+441
-437
@@ -13,7 +13,6 @@
|
|||||||
# - $FZF_ALT_C_COMMAND
|
# - $FZF_ALT_C_COMMAND
|
||||||
# - $FZF_ALT_C_OPTS
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
# The oldest supported fish version is 3.1b1. To maintain compatibility, the
|
# The oldest supported fish version is 3.1b1. To maintain compatibility, the
|
||||||
@@ -21,289 +20,289 @@
|
|||||||
# check, otherwise the source command will fail on fish versions older than 3.4.0.
|
# check, otherwise the source command will fail on fish versions older than 3.4.0.
|
||||||
function fzf_key_bindings
|
function fzf_key_bindings
|
||||||
|
|
||||||
# Check fish version
|
# Check fish version
|
||||||
if set -l -- fish_ver (string match -r '^(\d+)\.(\d+)' $version 2>/dev/null)
|
if set -l -- fish_ver (string match -r '^(\d+)\.(\d+)' $version 2>/dev/null)
|
||||||
and test "$fish_ver[2]" -lt 3 -o "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1
|
and test "$fish_ver[2]" -lt 3 -o "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1
|
||||||
echo "This script requires fish version 3.1b1 or newer." >&2
|
echo "This script requires fish version 3.1b1 or newer." >&2
|
||||||
return 1
|
return 1
|
||||||
else if not type -q fzf
|
else if not type -q fzf
|
||||||
echo "fzf was not found in path." >&2
|
echo "fzf was not found in path." >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
|
||||||
|
|
||||||
# bg-transform runs the preview toggle in a background thread (non-blocking).
|
|
||||||
# It was added after fzf 0.60; fall back to the synchronous transform on older builds.
|
|
||||||
set -l _fzf_ver (fzf --version | string match -r '^(\d+)\.(\d+)')
|
|
||||||
set -l _fzf_transform_action transform
|
|
||||||
if test -n "$_fzf_ver[3]"; and test "$_fzf_ver[3]" -ge 62
|
|
||||||
set _fzf_transform_action bg-transform
|
|
||||||
end
|
|
||||||
|
|
||||||
#----BEGIN INCLUDE common.fish
|
|
||||||
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
|
||||||
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
|
||||||
# the changes. See code comments in "common.fish" for the implementation details.
|
|
||||||
|
|
||||||
function __fzf_defaults
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
|
||||||
string join ' ' -- \
|
|
||||||
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
|
||||||
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
|
|
||||||
$FZF_DEFAULT_OPTS $argv[2..-1]
|
|
||||||
end
|
|
||||||
|
|
||||||
function __fzfcmd
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
|
||||||
if test -n "$FZF_TMUX_OPTS"
|
|
||||||
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
|
||||||
else if test "$FZF_TMUX" = "1"
|
|
||||||
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
|
||||||
else
|
|
||||||
echo "fzf"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
|
||||||
set -l tokens
|
|
||||||
if test (string match -r -- '^\d+' $version) -ge 4
|
|
||||||
set -- tokens (commandline -xpc)
|
|
||||||
else
|
|
||||||
set -- tokens (commandline -opc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l -- var_count 0
|
# bg-transform runs the preview toggle in a background thread (non-blocking).
|
||||||
for i in $tokens
|
# It was added after fzf 0.60; fall back to the synchronous transform on older builds.
|
||||||
if string match -qr -- '^[\w]+=' $i
|
set -l _fzf_ver (fzf --version | string match -r '^(\d+)\.(\d+)')
|
||||||
set var_count (math $var_count + 1)
|
set -l _fzf_transform_action transform
|
||||||
else
|
if test -n "$_fzf_ver[3]"; and test "$_fzf_ver[3]" -ge 62
|
||||||
break
|
set _fzf_transform_action bg-transform
|
||||||
end
|
|
||||||
end
|
|
||||||
set -e -- tokens[0..$var_count]
|
|
||||||
|
|
||||||
while true
|
|
||||||
switch "$tokens[1]"
|
|
||||||
case builtin command
|
|
||||||
set -e -- tokens[1]
|
|
||||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
|
||||||
case env
|
|
||||||
set -e -- tokens[1]
|
|
||||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
|
||||||
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
|
||||||
set -e -- tokens[1]
|
|
||||||
end
|
|
||||||
case '*'
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
string escape -n -- $tokens
|
#----BEGIN INCLUDE common.fish
|
||||||
end
|
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
||||||
|
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
||||||
|
# the changes. See code comments in "common.fish" for the implementation details.
|
||||||
|
|
||||||
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
function __fzf_defaults
|
||||||
set -l fzf_query ''
|
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
||||||
set -l prefix ''
|
string join ' ' -- \
|
||||||
set -l dir '.'
|
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
||||||
|
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
|
||||||
set -l -- fish_major (string match -r -- '^\d+' $version)
|
$FZF_DEFAULT_OPTS $argv[2..-1]
|
||||||
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
|
||||||
|
|
||||||
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
|
||||||
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
|
||||||
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
|
|
||||||
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if test "$fish_major" -ge 4
|
function __fzfcmd
|
||||||
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
|
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
||||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
if test -n "$FZF_TMUX_OPTS"
|
||||||
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
|
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
else if test "$FZF_TMUX" = 1
|
||||||
else
|
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
||||||
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
|
||||||
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
|
||||||
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
|
|
||||||
end
|
|
||||||
|
|
||||||
if test -n "$fzf_query"
|
|
||||||
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
|
||||||
set -- fzf_query (path normalize -- $fzf_query)
|
|
||||||
set -- dir $fzf_query
|
|
||||||
while not path is -d $dir
|
|
||||||
set -- dir (path dirname $dir)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
|
||||||
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
|
||||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
|
||||||
else
|
else
|
||||||
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
echo fzf
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
||||||
|
set -l tokens
|
||||||
|
if test (string match -r -- '^\d+' $version) -ge 4
|
||||||
|
set -- tokens (commandline -xpc)
|
||||||
|
else
|
||||||
|
set -- tokens (commandline -opc)
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l -- var_count 0
|
||||||
|
for i in $tokens
|
||||||
|
if string match -qr -- '^[\w]+=' $i
|
||||||
|
set var_count (math $var_count + 1)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set -e -- tokens[0..$var_count]
|
||||||
|
|
||||||
|
while true
|
||||||
|
switch "$tokens[1]"
|
||||||
|
case builtin command
|
||||||
|
set -e -- tokens[1]
|
||||||
|
test "$tokens[1]" = --; and set -e -- tokens[1]
|
||||||
|
case env
|
||||||
|
set -e -- tokens[1]
|
||||||
|
test "$tokens[1]" = --; and set -e -- tokens[1]
|
||||||
|
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
||||||
|
set -e -- tokens[1]
|
||||||
|
end
|
||||||
|
case '*'
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string escape -n -- $tokens
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
||||||
|
set -l fzf_query ''
|
||||||
|
set -l prefix ''
|
||||||
|
set -l dir '.'
|
||||||
|
|
||||||
|
set -l -- fish_major (string match -r -- '^\d+' $version)
|
||||||
|
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
||||||
|
|
||||||
|
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
||||||
|
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
||||||
|
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
||||||
|
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
|
||||||
|
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
||||||
end
|
end
|
||||||
set -- dir $fzf_query
|
|
||||||
while not test -d "$dir"
|
|
||||||
set -- dir (dirname -z -- "$dir" | string split0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
|
||||||
if test "$fish_major" -ge 4
|
if test "$fish_major" -ge 4
|
||||||
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
|
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
|
||||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
|
||||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
||||||
else
|
else
|
||||||
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
||||||
|
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
if test -n "$fzf_query"
|
||||||
|
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
||||||
|
set -- fzf_query (path normalize -- $fzf_query)
|
||||||
|
set -- dir $fzf_query
|
||||||
|
while not path is -d $dir
|
||||||
|
set -- dir (path dirname $dir)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
|
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||||
|
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||||
|
else
|
||||||
|
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
||||||
|
end
|
||||||
|
set -- dir $fzf_query
|
||||||
|
while not test -d "$dir"
|
||||||
|
set -- dir (dirname -z -- "$dir" | string split0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
||||||
|
if test "$fish_major" -ge 4
|
||||||
|
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
|
||||||
|
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
|
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||||
|
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||||
|
else
|
||||||
|
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
||||||
end
|
end
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
# Store current token in $dir as root for the 'find' command
|
||||||
end
|
function fzf-file-widget -d "List files and folders"
|
||||||
#----END INCLUDE
|
set -l commandline (__fzf_parse_commandline)
|
||||||
|
set -lx dir $commandline[1]
|
||||||
|
set -l fzf_query $commandline[2]
|
||||||
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
# Store current token in $dir as root for the 'find' command
|
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||||
function fzf-file-widget -d "List files and folders"
|
|
||||||
set -l commandline (__fzf_parse_commandline)
|
|
||||||
set -lx dir $commandline[1]
|
|
||||||
set -l fzf_query $commandline[2]
|
|
||||||
set -l prefix $commandline[3]
|
|
||||||
|
|
||||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
|
||||||
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
|
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
|
||||||
"--multi $FZF_CTRL_T_OPTS --print0")
|
"--multi $FZF_CTRL_T_OPTS --print0")
|
||||||
|
|
||||||
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
|
|
||||||
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
|
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
|
||||||
and commandline -rt -- (string join -- ' ' $prefix(string escape --no-quoted -- $result))' '
|
and commandline -rt -- (string join -- ' ' $prefix(string escape --no-quoted -- $result))' '
|
||||||
|
|
||||||
commandline -f repaint
|
commandline -f repaint
|
||||||
end
|
|
||||||
|
|
||||||
function fzf-history-widget --inherit-variable _fzf_transform_action -d "Show command history"
|
|
||||||
set -l -- command_line (commandline)
|
|
||||||
set -l -- current_line (commandline -L)
|
|
||||||
set -l -- total_lines (count $command_line)
|
|
||||||
set -l -- fzf_query (string escape -- $command_line[$current_line])
|
|
||||||
|
|
||||||
# These options require newer fzf; omit them on older builds
|
|
||||||
set -l _fzf_preview_wrap_sign ''
|
|
||||||
set -l _fzf_toggle_raw ''
|
|
||||||
if test "$_fzf_transform_action" = bg-transform
|
|
||||||
set _fzf_preview_wrap_sign ' --preview-wrap-sign="↳ "'
|
|
||||||
set _fzf_toggle_raw ,alt-r:toggle-raw
|
|
||||||
end
|
end
|
||||||
|
|
||||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '' \
|
function fzf-history-widget --inherit-variable _fzf_transform_action -d "Show command history"
|
||||||
|
set -l -- command_line (commandline)
|
||||||
|
set -l -- current_line (commandline -L)
|
||||||
|
set -l -- total_lines (count $command_line)
|
||||||
|
set -l -- fzf_query (string escape -- $command_line[$current_line])
|
||||||
|
|
||||||
|
# These options require newer fzf; omit them on older builds
|
||||||
|
set -l _fzf_preview_wrap_sign ''
|
||||||
|
set -l _fzf_toggle_raw ''
|
||||||
|
if test "$_fzf_transform_action" = bg-transform
|
||||||
|
set _fzf_preview_wrap_sign ' --preview-wrap-sign="↳ "'
|
||||||
|
set _fzf_toggle_raw ,alt-r:toggle-raw
|
||||||
|
end
|
||||||
|
|
||||||
|
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '' \
|
||||||
'--nth=2..,.. --scheme=history --multi --no-multi-line --no-wrap --wrap-sign="\t\t\t↳ "'$_fzf_preview_wrap_sign \
|
'--nth=2..,.. --scheme=history --multi --no-multi-line --no-wrap --wrap-sign="\t\t\t↳ "'$_fzf_preview_wrap_sign \
|
||||||
'--bind=\'shift-delete:execute-silent(for i in (string split0 -- <{+f}); eval builtin history delete --exact --case-sensitive -- (string escape -n -- $i | string replace -r "^\d*\\\\\\t" ""); end)+reload(eval $FZF_DEFAULT_COMMAND)\'' \
|
'--bind=\'shift-delete:execute-silent(for i in (string split0 -- <{+f}); eval builtin history delete --exact --case-sensitive -- (string escape -n -- $i | string replace -r "^\d*\\\\\\t" ""); end)+reload(eval $FZF_DEFAULT_COMMAND)\'' \
|
||||||
'--bind="alt-enter:become(string join0 -- (string collect -- {+2..} | fish_indent -i))"' \
|
'--bind="alt-enter:become(string join0 -- (string collect -- {+2..} | fish_indent -i))"' \
|
||||||
"--bind=ctrl-r:toggle-sort$_fzf_toggle_raw --highlight-line $FZF_CTRL_R_OPTS" \
|
"--bind=ctrl-r:toggle-sort$_fzf_toggle_raw --highlight-line $FZF_CTRL_R_OPTS" \
|
||||||
'--accept-nth=2.. --delimiter="\t" --tabstop=4 --read0 --print0 --with-shell='(status fish-path)\\ -c)
|
'--accept-nth=2.. --delimiter="\t" --tabstop=4 --read0 --print0 --with-shell='(status fish-path)\\ -c)
|
||||||
|
|
||||||
# Add dynamic preview options if preview command isn't already set by user
|
# Add dynamic preview options if preview command isn't already set by user
|
||||||
if string match -qvr -- '--preview[= ]' "$FZF_DEFAULT_OPTS"
|
if string match -qvr -- '--preview[= ]' "$FZF_DEFAULT_OPTS"
|
||||||
# Convert the highlighted timestamp using the date command if available
|
# Convert the highlighted timestamp using the date command if available
|
||||||
set -l -- date_cmd '{1}'
|
set -l -- date_cmd '{1}'
|
||||||
if type -q date
|
if type -q date
|
||||||
if date -d @0 '+%s' 2>/dev/null | string match -q 0
|
if date -d @0 '+%s' 2>/dev/null | string match -q 0
|
||||||
# GNU date
|
# GNU date
|
||||||
set -- date_cmd '(date -d @{1} \\"+%F %a %T\\")'
|
set -- date_cmd '(date -d @{1} \\"+%F %a %T\\")'
|
||||||
else if date -r 0 '+%s' 2>/dev/null | string match -q 0
|
else if date -r 0 '+%s' 2>/dev/null | string match -q 0
|
||||||
# BSD date
|
# BSD date
|
||||||
set -- date_cmd '(date -r {1} \\"+%F %a %T\\")'
|
set -- date_cmd '(date -r {1} \\"+%F %a %T\\")'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prepend the options to allow user customizations
|
||||||
|
if test "$_fzf_transform_action" = bg-transform
|
||||||
|
# Newer fzf: dynamic show/hide preview based on terminal width
|
||||||
|
set -p -- FZF_DEFAULT_OPTS \
|
||||||
|
'--bind="focus,resize:bg-transform:if test \\"$FZF_COLUMNS\\" -gt 100 -a \\\\( \\"$FZF_SELECT_COUNT\\" -gt 0 -o \\\\( -z \\"$FZF_WRAP\\" -a (string length -- {}) -gt (math $FZF_COLUMNS - 4) \\\\) -o (string collect -- {2..} | fish_indent | count) -gt 1 \\\\); echo show-preview; else echo hide-preview; end"' \
|
||||||
|
'--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \
|
||||||
|
'--preview-window="right,50%,wrap-word,follow,info,hidden"'
|
||||||
|
else
|
||||||
|
# Older fzf: static preview; focus/resize events not reliably supported
|
||||||
|
set -p -- FZF_DEFAULT_OPTS \
|
||||||
|
'--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \
|
||||||
|
'--preview-window="right,50%,wrap,follow,info"'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Prepend the options to allow user customizations
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
if test "$_fzf_transform_action" = bg-transform
|
|
||||||
# Newer fzf: dynamic show/hide preview based on terminal width
|
set -lx -- FZF_DEFAULT_COMMAND 'builtin history -z --show-time="%s%t"'
|
||||||
set -p -- FZF_DEFAULT_OPTS \
|
|
||||||
'--bind="focus,resize:bg-transform:if test \\"$FZF_COLUMNS\\" -gt 100 -a \\\\( \\"$FZF_SELECT_COUNT\\" -gt 0 -o \\\\( -z \\"$FZF_WRAP\\" -a (string length -- {}) -gt (math $FZF_COLUMNS - 4) \\\\) -o (string collect -- {2..} | fish_indent | count) -gt 1 \\\\); echo show-preview; else echo hide-preview; end"' \
|
# Enable syntax highlighting colors on fish v4.3.3 and newer
|
||||||
'--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \
|
if set -l -- v (string match -r -- '^(\d+)\.(\d+)(?:\.(\d+))?' $version)
|
||||||
'--preview-window="right,50%,wrap-word,follow,info,hidden"'
|
and test "$v[2]" -gt 4 -o "$v[2]" -eq 4 -a \
|
||||||
else
|
\( "$v[3]" -gt 3 -o "$v[3]" -eq 3 -a \
|
||||||
# Older fzf: static preview; focus/resize events not reliably supported
|
\( -n "$v[4]" -a "$v[4]" -ge 3 \) \)
|
||||||
set -p -- FZF_DEFAULT_OPTS \
|
|
||||||
'--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \
|
set -a -- FZF_DEFAULT_OPTS --ansi
|
||||||
'--preview-window="right,50%,wrap,follow,info"'
|
set -a -- FZF_DEFAULT_COMMAND '--color=always'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Merge history from other sessions before searching
|
||||||
|
test -z "$fish_private_mode"; and builtin history merge
|
||||||
|
|
||||||
|
if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0)
|
||||||
|
if test "$total_lines" -eq 1
|
||||||
|
commandline -- $result
|
||||||
|
else
|
||||||
|
set -l a (math $current_line - 1)
|
||||||
|
set -l b (math $current_line + 1)
|
||||||
|
commandline -- $command_line[1..$a] $result
|
||||||
|
commandline -a -- '' $command_line[$b..-1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
|
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
function fzf-cd-widget -d "Change directory"
|
||||||
|
set -l commandline (__fzf_parse_commandline)
|
||||||
|
set -lx dir $commandline[1]
|
||||||
|
set -l fzf_query $commandline[2]
|
||||||
|
set -l prefix $commandline[3]
|
||||||
|
|
||||||
set -lx -- FZF_DEFAULT_COMMAND 'builtin history -z --show-time="%s%t"'
|
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||||
|
|
||||||
# Enable syntax highlighting colors on fish v4.3.3 and newer
|
|
||||||
if set -l -- v (string match -r -- '^(\d+)\.(\d+)(?:\.(\d+))?' $version)
|
|
||||||
and test "$v[2]" -gt 4 -o "$v[2]" -eq 4 -a \
|
|
||||||
\( "$v[3]" -gt 3 -o "$v[3]" -eq 3 -a \
|
|
||||||
\( -n "$v[4]" -a "$v[4]" -ge 3 \) \)
|
|
||||||
|
|
||||||
set -a -- FZF_DEFAULT_OPTS '--ansi'
|
|
||||||
set -a -- FZF_DEFAULT_COMMAND '--color=always'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Merge history from other sessions before searching
|
|
||||||
test -z "$fish_private_mode"; and builtin history merge
|
|
||||||
|
|
||||||
if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0)
|
|
||||||
if test "$total_lines" -eq 1
|
|
||||||
commandline -- $result
|
|
||||||
else
|
|
||||||
set -l a (math $current_line - 1)
|
|
||||||
set -l b (math $current_line + 1)
|
|
||||||
commandline -- $command_line[1..$a] $result
|
|
||||||
commandline -a -- '' $command_line[$b..-1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
commandline -f repaint
|
|
||||||
end
|
|
||||||
|
|
||||||
function fzf-cd-widget -d "Change directory"
|
|
||||||
set -l commandline (__fzf_parse_commandline)
|
|
||||||
set -lx dir $commandline[1]
|
|
||||||
set -l fzf_query $commandline[2]
|
|
||||||
set -l prefix $commandline[3]
|
|
||||||
|
|
||||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
|
||||||
"--reverse --walker=dir,follow,hidden --scheme=path" \
|
"--reverse --walker=dir,follow,hidden --scheme=path" \
|
||||||
"$FZF_ALT_C_OPTS --no-multi --print0")
|
"$FZF_ALT_C_OPTS --no-multi --print0")
|
||||||
|
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
||||||
|
|
||||||
if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0)
|
if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0)
|
||||||
cd -- $result
|
cd -- $result
|
||||||
commandline -rt -- $prefix
|
commandline -rt -- $prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
|
|
||||||
commandline -f repaint
|
if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND"
|
||||||
end
|
if test -n "$FZF_CTRL_R_COMMAND"
|
||||||
|
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
|
||||||
if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND"
|
end
|
||||||
if test -n "$FZF_CTRL_R_COMMAND"
|
bind \cr fzf-history-widget
|
||||||
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
|
bind -M insert \cr fzf-history-widget
|
||||||
end
|
end
|
||||||
bind \cr fzf-history-widget
|
|
||||||
bind -M insert \cr fzf-history-widget
|
|
||||||
end
|
|
||||||
|
|
||||||
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||||
bind \ct fzf-file-widget
|
bind \ct fzf-file-widget
|
||||||
bind -M insert \ct fzf-file-widget
|
bind -M insert \ct fzf-file-widget
|
||||||
end
|
end
|
||||||
|
|
||||||
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
|
||||||
bind \ec fzf-cd-widget
|
bind \ec fzf-cd-widget
|
||||||
bind -M insert \ec fzf-cd-widget
|
bind -M insert \ec fzf-cd-widget
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -321,233 +320,238 @@ fzf_key_bindings
|
|||||||
|
|
||||||
function fzf_completion_setup
|
function fzf_completion_setup
|
||||||
|
|
||||||
#----BEGIN INCLUDE common.fish
|
#----BEGIN INCLUDE common.fish
|
||||||
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
||||||
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
||||||
# the changes. See code comments in "common.fish" for the implementation details.
|
# the changes. See code comments in "common.fish" for the implementation details.
|
||||||
|
|
||||||
function __fzf_defaults
|
function __fzf_defaults
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
||||||
string join ' ' -- \
|
string join ' ' -- \
|
||||||
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
||||||
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
|
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
|
||||||
$FZF_DEFAULT_OPTS $argv[2..-1]
|
$FZF_DEFAULT_OPTS $argv[2..-1]
|
||||||
end
|
|
||||||
|
|
||||||
function __fzfcmd
|
|
||||||
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
|
||||||
if test -n "$FZF_TMUX_OPTS"
|
|
||||||
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
|
||||||
else if test "$FZF_TMUX" = "1"
|
|
||||||
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
|
||||||
else
|
|
||||||
echo "fzf"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
|
||||||
set -l tokens
|
|
||||||
if test (string match -r -- '^\d+' $version) -ge 4
|
|
||||||
set -- tokens (commandline -xpc)
|
|
||||||
else
|
|
||||||
set -- tokens (commandline -opc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l -- var_count 0
|
function __fzfcmd
|
||||||
for i in $tokens
|
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
||||||
if string match -qr -- '^[\w]+=' $i
|
if test -n "$FZF_TMUX_OPTS"
|
||||||
set var_count (math $var_count + 1)
|
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
||||||
else
|
else if test "$FZF_TMUX" = 1
|
||||||
break
|
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
||||||
end
|
|
||||||
end
|
|
||||||
set -e -- tokens[0..$var_count]
|
|
||||||
|
|
||||||
while true
|
|
||||||
switch "$tokens[1]"
|
|
||||||
case builtin command
|
|
||||||
set -e -- tokens[1]
|
|
||||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
|
||||||
case env
|
|
||||||
set -e -- tokens[1]
|
|
||||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
|
||||||
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
|
||||||
set -e -- tokens[1]
|
|
||||||
end
|
|
||||||
case '*'
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
string escape -n -- $tokens
|
|
||||||
end
|
|
||||||
|
|
||||||
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
|
||||||
set -l fzf_query ''
|
|
||||||
set -l prefix ''
|
|
||||||
set -l dir '.'
|
|
||||||
|
|
||||||
set -l -- fish_major (string match -r -- '^\d+' $version)
|
|
||||||
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
|
||||||
|
|
||||||
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
|
||||||
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
|
||||||
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
|
|
||||||
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$fish_major" -ge 4
|
|
||||||
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
|
|
||||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
|
||||||
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
|
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
|
||||||
else
|
|
||||||
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
|
||||||
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
|
||||||
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
|
|
||||||
end
|
|
||||||
|
|
||||||
if test -n "$fzf_query"
|
|
||||||
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
|
||||||
set -- fzf_query (path normalize -- $fzf_query)
|
|
||||||
set -- dir $fzf_query
|
|
||||||
while not path is -d $dir
|
|
||||||
set -- dir (path dirname $dir)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
|
||||||
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
|
||||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
|
||||||
else
|
else
|
||||||
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
echo fzf
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
||||||
|
set -l tokens
|
||||||
|
if test (string match -r -- '^\d+' $version) -ge 4
|
||||||
|
set -- tokens (commandline -xpc)
|
||||||
|
else
|
||||||
|
set -- tokens (commandline -opc)
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l -- var_count 0
|
||||||
|
for i in $tokens
|
||||||
|
if string match -qr -- '^[\w]+=' $i
|
||||||
|
set var_count (math $var_count + 1)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set -e -- tokens[0..$var_count]
|
||||||
|
|
||||||
|
while true
|
||||||
|
switch "$tokens[1]"
|
||||||
|
case builtin command
|
||||||
|
set -e -- tokens[1]
|
||||||
|
test "$tokens[1]" = --; and set -e -- tokens[1]
|
||||||
|
case env
|
||||||
|
set -e -- tokens[1]
|
||||||
|
test "$tokens[1]" = --; and set -e -- tokens[1]
|
||||||
|
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
||||||
|
set -e -- tokens[1]
|
||||||
|
end
|
||||||
|
case '*'
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string escape -n -- $tokens
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
||||||
|
set -l fzf_query ''
|
||||||
|
set -l prefix ''
|
||||||
|
set -l dir '.'
|
||||||
|
|
||||||
|
set -l -- fish_major (string match -r -- '^\d+' $version)
|
||||||
|
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
||||||
|
|
||||||
|
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
||||||
|
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
||||||
|
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
||||||
|
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
|
||||||
|
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
||||||
end
|
end
|
||||||
set -- dir $fzf_query
|
|
||||||
while not test -d "$dir"
|
|
||||||
set -- dir (dirname -z -- "$dir" | string split0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
|
||||||
if test "$fish_major" -ge 4
|
if test "$fish_major" -ge 4
|
||||||
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
|
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
|
||||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
|
||||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
||||||
else
|
else
|
||||||
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
||||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
||||||
|
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
if test -n "$fzf_query"
|
||||||
|
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
||||||
|
set -- fzf_query (path normalize -- $fzf_query)
|
||||||
|
set -- dir $fzf_query
|
||||||
|
while not path is -d $dir
|
||||||
|
set -- dir (path dirname $dir)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
|
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||||
|
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||||
|
else
|
||||||
|
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
||||||
|
end
|
||||||
|
set -- dir $fzf_query
|
||||||
|
while not test -d "$dir"
|
||||||
|
set -- dir (dirname -z -- "$dir" | string split0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
||||||
|
if test "$fish_major" -ge 4
|
||||||
|
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
|
||||||
|
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||||
|
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||||
|
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||||
|
else
|
||||||
|
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||||
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
||||||
end
|
end
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
# Use complete builtin for specific commands
|
||||||
end
|
function __fzf_complete_native
|
||||||
#----END INCLUDE
|
set -l -- token (commandline -t)
|
||||||
|
set -l -- completions (eval complete -C \"$argv[1]\")
|
||||||
|
test -n "$completions"; or begin
|
||||||
|
commandline -f repaint
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# Use complete builtin for specific commands
|
# Calculate tabstop based on longest completion item (sample first 500 for performance)
|
||||||
function __fzf_complete_native
|
set -l -- tabstop 20
|
||||||
set -l -- token (commandline -t)
|
set -l -- sample_size (math "min(500, "(count $completions)")")
|
||||||
set -l -- completions (eval complete -C \"$argv[1]\")
|
for c in $completions[1..$sample_size]
|
||||||
test -n "$completions"; or begin commandline -f repaint; return; end
|
set -l -- len (string length -V -- (string split -- \t $c))
|
||||||
|
test -n "$len[2]" -a "$len[1]" -gt "$tabstop"
|
||||||
|
and set -- tabstop $len[1]
|
||||||
|
end
|
||||||
|
# limit to 120 to prevent long lines
|
||||||
|
set -- tabstop (math "min($tabstop + 4, 120)")
|
||||||
|
|
||||||
# Calculate tabstop based on longest completion item (sample first 500 for performance)
|
set -l result
|
||||||
set -l -- tabstop 20
|
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||||
set -l -- sample_size (math "min(500, "(count $completions)")")
|
|
||||||
for c in $completions[1..$sample_size]
|
|
||||||
set -l -- len (string length -V -- (string split -- \t $c))
|
|
||||||
test -n "$len[2]" -a "$len[1]" -gt "$tabstop"
|
|
||||||
and set -- tabstop $len[1]
|
|
||||||
end
|
|
||||||
# limit to 120 to prevent long lines
|
|
||||||
set -- tabstop (math "min($tabstop + 4, 120)")
|
|
||||||
|
|
||||||
set -l result
|
|
||||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults \
|
|
||||||
"--reverse --delimiter=\\t --nth=1 --tabstop=$tabstop --color=fg:dim,nth:regular" \
|
"--reverse --delimiter=\\t --nth=1 --tabstop=$tabstop --color=fg:dim,nth:regular" \
|
||||||
$FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1 --read0 --print0)
|
$FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1 --read0 --print0)
|
||||||
set -- result (string join0 -- $completions | eval (__fzfcmd) | string split0)
|
set -- result (string join0 -- $completions | eval (__fzfcmd) | string split0)
|
||||||
and begin
|
and begin
|
||||||
set -l -- tail ' '
|
set -l -- tail ' '
|
||||||
# Append / to bare ~username results (fish omits it unlike other shells)
|
# Append / to bare ~username results (fish omits it unlike other shells)
|
||||||
set -- result (string replace -r -- '^(~\w+)\s?$' '$1/' $result)
|
set -- result (string replace -r -- '^(~\w+)\s?$' '$1/' $result)
|
||||||
# Don't add trailing space if single result is a directory
|
# Don't add trailing space if single result is a directory
|
||||||
test (count $result) -eq 1
|
test (count $result) -eq 1
|
||||||
and string match -q -- '*/' "$result"; and set -- tail ''
|
and string match -q -- '*/' "$result"; and set -- tail ''
|
||||||
|
|
||||||
set -l -- result (string escape -n -- $result)
|
set -l -- result (string escape -n -- $result)
|
||||||
|
|
||||||
string match -q -- '~*' "$token"
|
string match -q -- '~*' "$token"
|
||||||
and set result (string replace -r -- '^\\\\~' '~' $result)
|
and set result (string replace -r -- '^\\\\~' '~' $result)
|
||||||
|
|
||||||
string match -q -- '$*' "$token"
|
string match -q -- '$*' "$token"
|
||||||
and set result (string replace -r -- '^\\\\\$' '\$' $result)
|
and set result (string replace -r -- '^\\\\\$' '\$' $result)
|
||||||
|
|
||||||
commandline -rt -- (string join ' ' -- $result)$tail
|
commandline -rt -- (string join ' ' -- $result)$tail
|
||||||
|
end
|
||||||
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
commandline -f repaint
|
|
||||||
end
|
|
||||||
|
|
||||||
function _fzf_complete
|
function _fzf_complete
|
||||||
set -l -- args (string escape -- $argv | string join ' ' | string split -- ' -- ')
|
set -l -- args (string escape -- $argv | string join ' ' | string split -- ' -- ')
|
||||||
set -l -- post_func (status function)_(string split -- ' ' $args[2])[1]_post
|
set -l -- post_func (status function)_(string split -- ' ' $args[2])[1]_post
|
||||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS $args[1])
|
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS $args[1])
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
set -lx FZF_DEFAULT_COMMAND
|
set -lx FZF_DEFAULT_COMMAND
|
||||||
set -l -- fzf_query (commandline -t | string escape)
|
set -l -- fzf_query (commandline -t | string escape)
|
||||||
set -l result
|
set -l result
|
||||||
eval (__fzfcmd) --query=$fzf_query | while read -l r; set -a -- result $r; end
|
eval (__fzfcmd) --query=$fzf_query | while read -l r
|
||||||
and if functions -q $post_func
|
set -a -- result $r
|
||||||
commandline -rt -- (string collect -- $result | eval $post_func $args[2] | string join ' ')' '
|
end
|
||||||
else
|
and if functions -q $post_func
|
||||||
commandline -rt -- (string join -- ' ' (string escape -- $result))' '
|
commandline -rt -- (string collect -- $result | eval $post_func $args[2] | string join ' ')' '
|
||||||
|
else
|
||||||
|
commandline -rt -- (string join -- ' ' (string escape -- $result))' '
|
||||||
|
end
|
||||||
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
commandline -f repaint
|
|
||||||
end
|
|
||||||
|
|
||||||
# Kill completion (process selection)
|
# Kill completion (process selection)
|
||||||
function _fzf_complete_kill
|
function _fzf_complete_kill
|
||||||
set -l -- fzf_query (commandline -t | string escape)
|
set -l -- fzf_query (commandline -t | string escape)
|
||||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS \
|
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS \
|
||||||
--accept-nth=2 -m --header-lines=1 --no-preview --wrap)
|
--accept-nth=2 -m --header-lines=1 --no-preview --wrap)
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
if type -q ps
|
if type -q ps
|
||||||
set -l -- ps_cmd 'begin command ps -eo user,pid,ppid,start,time,command 2>/dev/null;' \
|
set -l -- ps_cmd 'begin command ps -eo user,pid,ppid,start,time,command 2>/dev/null;' \
|
||||||
'or command ps -eo user,pid,ppid,time,args 2>/dev/null;' \
|
'or command ps -eo user,pid,ppid,time,args 2>/dev/null;' \
|
||||||
'or command ps --everyone --full --windows 2>/dev/null; end'
|
'or command ps --everyone --full --windows 2>/dev/null; end'
|
||||||
set -l -- result (eval $ps_cmd \| (__fzfcmd) --query=$fzf_query)
|
set -l -- result (eval $ps_cmd \| (__fzfcmd) --query=$fzf_query)
|
||||||
and commandline -rt -- (string join ' ' -- $result)" "
|
and commandline -rt -- (string join ' ' -- $result)" "
|
||||||
else
|
else
|
||||||
__fzf_complete_native "kill " --multi --query=$fzf_query
|
__fzf_complete_native "kill " --multi --query=$fzf_query
|
||||||
|
end
|
||||||
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
commandline -f repaint
|
|
||||||
end
|
|
||||||
|
|
||||||
# Main completion function
|
# Main completion function
|
||||||
function fzf-completion
|
function fzf-completion
|
||||||
set -l -- tokens (__fzf_cmd_tokens)
|
set -l -- tokens (__fzf_cmd_tokens)
|
||||||
set -l -- current_token (commandline -t)
|
set -l -- current_token (commandline -t)
|
||||||
set -l -- cmd_name $tokens[1]
|
set -l -- cmd_name $tokens[1]
|
||||||
|
|
||||||
# Route to appropriate completion function
|
# Route to appropriate completion function
|
||||||
if test -n "$tokens"; and functions -q _fzf_complete_$cmd_name
|
if test -n "$tokens"; and functions -q _fzf_complete_$cmd_name
|
||||||
_fzf_complete_$cmd_name $tokens
|
_fzf_complete_$cmd_name $tokens
|
||||||
else
|
else
|
||||||
set -l -- fzf_opt --query=$current_token --multi
|
set -l -- fzf_opt --query=$current_token --multi
|
||||||
__fzf_complete_native "$tokens $current_token" $fzf_opt
|
__fzf_complete_native "$tokens $current_token" $fzf_opt
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Bind Shift-Tab to fzf-completion (Tab retains native Fish behavior)
|
# Bind Shift-Tab to fzf-completion (Tab retains native Fish behavior)
|
||||||
if test (string match -r -- '^\d+' $version) -ge 4
|
if test (string match -r -- '^\d+' $version) -ge 4
|
||||||
bind shift-tab fzf-completion
|
bind shift-tab fzf-completion
|
||||||
bind -M insert shift-tab fzf-completion
|
bind -M insert shift-tab fzf-completion
|
||||||
else
|
else
|
||||||
bind -k btab fzf-completion
|
bind -k btab fzf-completion
|
||||||
bind -M insert -k btab fzf-completion
|
bind -M insert -k btab fzf-completion
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run setup
|
# Run setup
|
||||||
|
|||||||
@@ -24,9 +24,12 @@ set -l mode bytes
|
|||||||
set -l baseline main
|
set -l baseline main
|
||||||
for i in (seq (count $argv))
|
for i in (seq (count $argv))
|
||||||
switch $argv[$i]
|
switch $argv[$i]
|
||||||
case --structural; set mode structural
|
case --structural
|
||||||
case --startup; set mode startup
|
set mode structural
|
||||||
case --baseline; set baseline $argv[(math $i + 1)]
|
case --startup
|
||||||
|
set mode startup
|
||||||
|
case --baseline
|
||||||
|
set baseline $argv[(math $i + 1)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -36,8 +39,8 @@ function __pb_cleanup --on-event fish_exit --inherit-variable tmp
|
|||||||
end
|
end
|
||||||
|
|
||||||
# ── Build the two sandboxes ────────────────────────────────────────────
|
# ── Build the two sandboxes ────────────────────────────────────────────
|
||||||
set -l A $tmp/base/fish # pristine baseline ref
|
set -l A $tmp/base/fish # pristine baseline ref
|
||||||
set -l B $tmp/work/fish # current working tree
|
set -l B $tmp/work/fish # current working tree
|
||||||
mkdir -p $A $B
|
mkdir -p $A $B
|
||||||
git -C $repo archive $baseline | tar -x -C $A
|
git -C $repo archive $baseline | tar -x -C $A
|
||||||
or begin
|
or begin
|
||||||
@@ -71,7 +74,7 @@ set -l cases \
|
|||||||
"p --help" "pkg --help" "play-media --help" "qc --help" \
|
"p --help" "pkg --help" "play-media --help" "qc --help" \
|
||||||
"rand_string --help" "replay --help" "repo-open --help" "scrub --help" \
|
"rand_string --help" "replay --help" "repo-open --help" "scrub --help" \
|
||||||
"smart_exit --help" "spark --help" "y --help" \
|
"smart_exit --help" "spark --help" "y --help" \
|
||||||
"mkcd" "auto-pull remove __no_such_repo__" \
|
mkcd "auto-pull remove __no_such_repo__" \
|
||||||
"agents-init --no-such-flag" "pkg __no_such_subcommand__"
|
"agents-init --no-such-flag" "pkg __no_such_subcommand__"
|
||||||
|
|
||||||
function __pb_run --argument-names cfg stub cmd out
|
function __pb_run --argument-names cfg stub cmd out
|
||||||
|
|||||||
+17
-11
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# CI test runner for this fish configuration.
|
# CI test runner for this fish configuration.
|
||||||
# 1. Syntax-lints every tracked .fish file (fish -n).
|
# 1. Syntax-lints and indent-checks every tracked .fish file (fish -n, fish_indent --check).
|
||||||
# 2. Discovers tests/test-*.fish and reads the mode each suite declares
|
# 2. Discovers tests/test-*.fish and reads the mode each suite declares
|
||||||
# in its own header (`# MODE: isolated` or `# MODE: in-session`).
|
# in its own header (`# MODE: isolated` or `# MODE: in-session`).
|
||||||
# 3. Runs each isolated suite as its own --no-config fish process with
|
# 3. Runs each isolated suite as its own --no-config fish process with
|
||||||
@@ -24,25 +24,32 @@ set -l script_dir (realpath (dirname (status filename)))
|
|||||||
set -l repo_root (realpath $script_dir/..)
|
set -l repo_root (realpath $script_dir/..)
|
||||||
set -l overall_failed 0
|
set -l overall_failed 0
|
||||||
|
|
||||||
# ---- Phase 1: syntax lint ------------------------------------------------
|
# ---- Phase 1: syntax & indent lint ---------------------------------------
|
||||||
echo "== Syntax lint =="
|
echo "== Syntax & indent lint =="
|
||||||
set -l lint_files $repo_root/config.fish
|
set -l lint_files $repo_root/config.fish
|
||||||
for dir in functions conf.d completions integrations
|
for dir in functions conf.d completions integrations tests
|
||||||
set -a lint_files (find $repo_root/$dir -name '*.fish' | sort)
|
set -a lint_files (find $repo_root/$dir -name '*.fish' | sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l lint_failed 0
|
set -l syntax_failed 0
|
||||||
|
set -l indent_failed 0
|
||||||
for f in $lint_files
|
for f in $lint_files
|
||||||
set -l out (fish -n $f 2>&1)
|
set -l out (fish -n $f 2>&1)
|
||||||
if test $status -ne 0
|
if test $status -ne 0
|
||||||
echo " FAIL "(string replace $repo_root/ '' $f)
|
echo " FAIL (syntax) "(string replace $repo_root/ '' $f)
|
||||||
printf '%s\n' $out
|
printf '%s\n' $out
|
||||||
set lint_failed (math $lint_failed + 1)
|
set syntax_failed (math $syntax_failed + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fish_indent --check $f >/dev/null 2>&1
|
||||||
|
echo " FAIL (indent) "(string replace $repo_root/ '' $f)
|
||||||
|
set indent_failed (math $indent_failed + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
set -l lint_total (count $lint_files)
|
set -l lint_total (count $lint_files)
|
||||||
echo (math $lint_total - $lint_failed)"/$lint_total files passed lint"
|
echo (math $lint_total - $syntax_failed)"/$lint_total files passed syntax check"
|
||||||
if test $lint_failed -ne 0
|
echo (math $lint_total - $indent_failed)"/$lint_total files passed indent check"
|
||||||
|
if test $syntax_failed -ne 0 -o $indent_failed -ne 0
|
||||||
set overall_failed 1
|
set overall_failed 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -160,8 +167,7 @@ if test (count $session_suites) -gt 0
|
|||||||
__fish_config_op_autoexec=off \
|
__fish_config_op_autoexec=off \
|
||||||
FISH_CONFIG_TEST_ROOT=$repo_root \
|
FISH_CONFIG_TEST_ROOT=$repo_root \
|
||||||
FISH_CONFIG_TEST_COUNTS=$counts \
|
FISH_CONFIG_TEST_COUNTS=$counts \
|
||||||
fish -i -c "source $repo_root/tests/lib.fish; $srcs report" \
|
fish -i -c "source $repo_root/tests/lib.fish; $srcs report" 2>$err_file
|
||||||
2>$err_file
|
|
||||||
set -l session_status $status
|
set -l session_status $status
|
||||||
|
|
||||||
set -l stderr_out (command cat $err_file)
|
set -l stderr_out (command cat $err_file)
|
||||||
|
|||||||
+197
-109
@@ -70,7 +70,7 @@ function failing_shim --argument-names name match
|
|||||||
'#!/bin/sh' \
|
'#!/bin/sh' \
|
||||||
'for a in "$@"; do' \
|
'for a in "$@"; do' \
|
||||||
" case \"\$a\" in *$match*) exit 1 ;; esac" \
|
" case \"\$a\" in *$match*) exit 1 ;; esac" \
|
||||||
'done' \
|
done \
|
||||||
"exec $real \"\$@\"" >$d/$name
|
"exec $real \"\$@\"" >$d/$name
|
||||||
chmod +x $d/$name
|
chmod +x $d/$name
|
||||||
printf '%s\n' $d
|
printf '%s\n' $d
|
||||||
@@ -166,7 +166,8 @@ git -C $r3 remote add upstream https://git.rootiest.dev/rootiest/fish-config.git
|
|||||||
check "falls back to first remote" $want (_agents_repo_slug $r3)
|
check "falls back to first remote" $want (_agents_repo_slug $r3)
|
||||||
|
|
||||||
# Slug sanitization test: special chars in fallback (local-) branch.
|
# Slug sanitization test: special chars in fallback (local-) branch.
|
||||||
set -l dirt (mktemp -d); set -ga TMPDIRS $dirt
|
set -l dirt (mktemp -d)
|
||||||
|
set -ga TMPDIRS $dirt
|
||||||
mkdir -p "$dirt/projects/My Project!"
|
mkdir -p "$dirt/projects/My Project!"
|
||||||
git -C "$dirt/projects/My Project!" init -q
|
git -C "$dirt/projects/My Project!" init -q
|
||||||
git -C "$dirt/projects/My Project!" config user.email t@t
|
git -C "$dirt/projects/My Project!" config user.email t@t
|
||||||
@@ -179,7 +180,8 @@ check "sanitizes special chars in local slug" false "$has_bad_chars"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== _agents_repo_ensure_symlink =="
|
echo "== _agents_repo_ensure_symlink =="
|
||||||
|
|
||||||
set -l w (mktemp -d); set -ga TMPDIRS $w
|
set -l w (mktemp -d)
|
||||||
|
set -ga TMPDIRS $w
|
||||||
mkdir -p $w/target $w/live
|
mkdir -p $w/target $w/live
|
||||||
|
|
||||||
# Fresh link onto an empty live parent.
|
# Fresh link onto an empty live parent.
|
||||||
@@ -200,7 +202,8 @@ check "refuses missing target" 1 (_agents_repo_ensure_symlink $w/live/f3 $w/nope
|
|||||||
check "no dangling link left" false (test -L $w/live/f3; and echo true; or echo false)
|
check "no dangling link left" false (test -L $w/live/f3; and echo true; or echo false)
|
||||||
|
|
||||||
# Non-destructive adoption: content on both sides, nothing overwritten.
|
# Non-destructive adoption: content on both sides, nothing overwritten.
|
||||||
set -l a (mktemp -d); set -ga TMPDIRS $a
|
set -l a (mktemp -d)
|
||||||
|
set -ga TMPDIRS $a
|
||||||
mkdir -p $a/vault $a/live/memory
|
mkdir -p $a/vault $a/live/memory
|
||||||
echo vault-version >$a/vault/shared.md
|
echo vault-version >$a/vault/shared.md
|
||||||
echo vault-only >$a/vault/vaultonly.md
|
echo vault-only >$a/vault/vaultonly.md
|
||||||
@@ -213,7 +216,8 @@ check "adoption keeps vault-only file" vault-only (cat $a/vault/vaultonly.md)
|
|||||||
check "adoption replaced dir with link" true (test -L $a/live/memory; and echo true; or echo false)
|
check "adoption replaced dir with link" true (test -L $a/live/memory; and echo true; or echo false)
|
||||||
|
|
||||||
# Repins a link that points somewhere else.
|
# Repins a link that points somewhere else.
|
||||||
set -l p (mktemp -d); set -ga TMPDIRS $p
|
set -l p (mktemp -d)
|
||||||
|
set -ga TMPDIRS $p
|
||||||
mkdir -p $p/one $p/two
|
mkdir -p $p/one $p/two
|
||||||
ln -s $p/one $p/link
|
ln -s $p/one $p/link
|
||||||
_agents_repo_ensure_symlink $p/link $p/two >/dev/null
|
_agents_repo_ensure_symlink $p/link $p/two >/dev/null
|
||||||
@@ -239,12 +243,14 @@ check "idempotent, silent" "" "$out"
|
|||||||
# guarantee, and the old pull-first shape broke it: a failed fetch took
|
# guarantee, and the old pull-first shape broke it: a failed fetch took
|
||||||
# the local commit down with it. The divergence is still built here so
|
# the local commit down with it. The divergence is still built here so
|
||||||
# that guarantee is tested against the case that used to fail.
|
# that guarantee is tested against the case that used to fail.
|
||||||
set -l origin (mktemp -d); set -ga TMPDIRS $origin
|
set -l origin (mktemp -d)
|
||||||
|
set -ga TMPDIRS $origin
|
||||||
git -C $origin init -q --bare
|
git -C $origin init -q --bare
|
||||||
git -C $s remote add origin $origin
|
git -C $s remote add origin $origin
|
||||||
git -C $s push -q -u origin HEAD:refs/heads/main 2>/dev/null
|
git -C $s push -q -u origin HEAD:refs/heads/main 2>/dev/null
|
||||||
|
|
||||||
set -l clone (mktemp -d); set -ga TMPDIRS $clone
|
set -l clone (mktemp -d)
|
||||||
|
set -ga TMPDIRS $clone
|
||||||
git clone -q $origin $clone
|
git clone -q $origin $clone
|
||||||
git -C $clone config user.email t@t
|
git -C $clone config user.email t@t
|
||||||
git -C $clone config user.name t
|
git -C $clone config user.name t
|
||||||
@@ -297,7 +303,8 @@ set -l h (new_repo)
|
|||||||
echo first >$h/a.md
|
echo first >$h/a.md
|
||||||
_agents_repo_sync $h "chore: init" >/dev/null
|
_agents_repo_sync $h "chore: init" >/dev/null
|
||||||
|
|
||||||
set -l hooks (mktemp -d); set -ga TMPDIRS $hooks
|
set -l hooks (mktemp -d)
|
||||||
|
set -ga TMPDIRS $hooks
|
||||||
printf '#!/bin/sh\nexit 1\n' >$hooks/pre-commit
|
printf '#!/bin/sh\nexit 1\n' >$hooks/pre-commit
|
||||||
chmod +x $hooks/pre-commit
|
chmod +x $hooks/pre-commit
|
||||||
git -C $h config core.hooksPath $hooks
|
git -C $h config core.hooksPath $hooks
|
||||||
@@ -312,8 +319,10 @@ check "commit hook rejection commits nothing" $before_hook_count (git -C $h rev-
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (scaffold + link) =="
|
echo "== agents-vault (scaffold + link) =="
|
||||||
|
|
||||||
set -l vroot (mktemp -d); set -ga TMPDIRS $vroot
|
set -l vroot (mktemp -d)
|
||||||
set -l croot (mktemp -d); set -ga TMPDIRS $croot
|
set -ga TMPDIRS $vroot
|
||||||
|
set -l croot (mktemp -d)
|
||||||
|
set -ga TMPDIRS $croot
|
||||||
set -g __fish_agent_vault_dir $vroot/agent-vault
|
set -g __fish_agent_vault_dir $vroot/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot
|
set -g __fish_agent_vault_claude_root $croot
|
||||||
|
|
||||||
@@ -359,8 +368,10 @@ set -e __fish_agent_vault_claude_root
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (emergent restore) =="
|
echo "== agents-vault (emergent restore) =="
|
||||||
|
|
||||||
set -l vroot2 (mktemp -d); set -ga TMPDIRS $vroot2
|
set -l vroot2 (mktemp -d)
|
||||||
set -l croot2 (mktemp -d); set -ga TMPDIRS $croot2
|
set -ga TMPDIRS $vroot2
|
||||||
|
set -l croot2 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $croot2
|
||||||
set -g __fish_agent_vault_dir $vroot2/agent-vault
|
set -g __fish_agent_vault_dir $vroot2/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot2
|
set -g __fish_agent_vault_claude_root $croot2
|
||||||
|
|
||||||
@@ -393,8 +404,10 @@ set -e __fish_agent_vault_claude_root
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (link failure surfaces as exit 1) =="
|
echo "== agents-vault (link failure surfaces as exit 1) =="
|
||||||
|
|
||||||
set -l vroot3 (mktemp -d); set -ga TMPDIRS $vroot3
|
set -l vroot3 (mktemp -d)
|
||||||
set -l croot3 (mktemp -d); set -ga TMPDIRS $croot3
|
set -ga TMPDIRS $vroot3
|
||||||
|
set -l croot3 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $croot3
|
||||||
set -g __fish_agent_vault_dir $vroot3/agent-vault
|
set -g __fish_agent_vault_dir $vroot3/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot3
|
set -g __fish_agent_vault_claude_root $croot3
|
||||||
|
|
||||||
@@ -420,8 +433,10 @@ set -e __fish_agent_vault_claude_root
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (slug migration) =="
|
echo "== agents-vault (slug migration) =="
|
||||||
|
|
||||||
set -l vroot2 (mktemp -d); set -ga TMPDIRS $vroot2
|
set -l vroot2 (mktemp -d)
|
||||||
set -l croot2 (mktemp -d); set -ga TMPDIRS $croot2
|
set -ga TMPDIRS $vroot2
|
||||||
|
set -l croot2 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $croot2
|
||||||
set -g __fish_agent_vault_dir $vroot2/agent-vault
|
set -g __fish_agent_vault_dir $vroot2/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot2
|
set -g __fish_agent_vault_claude_root $croot2
|
||||||
|
|
||||||
@@ -429,7 +444,7 @@ set -g __fish_agent_vault_claude_root $croot2
|
|||||||
set -l mp (new_repo)
|
set -l mp (new_repo)
|
||||||
set -l mmangled (string replace -a '/' '-' -- $mp | string replace -a '.' '-')
|
set -l mmangled (string replace -a '/' '-' -- $mp | string replace -a '.' '-')
|
||||||
mkdir -p $croot2/$mmangled/memory
|
mkdir -p $croot2/$mmangled/memory
|
||||||
echo "precious" >$croot2/$mmangled/memory/keep.md
|
echo precious >$croot2/$mmangled/memory/keep.md
|
||||||
|
|
||||||
pushd $mp >/dev/null
|
pushd $mp >/dev/null
|
||||||
agents-vault --silent
|
agents-vault --silent
|
||||||
@@ -495,8 +510,7 @@ set -l enew_slug git.rootiest.dev-rootiest-emptycase
|
|||||||
# hand-built shape is covered separately just below.
|
# hand-built shape is covered separately just below.
|
||||||
mkdir -p $vroot2/agent-vault/projects/$enew_slug
|
mkdir -p $vroot2/agent-vault/projects/$enew_slug
|
||||||
printf 'remote: %s\npath: %s\nhost: %s\n' \
|
printf 'remote: %s\npath: %s\nhost: %s\n' \
|
||||||
https://git.rootiest.dev/rootiest/emptycase.git /gone/elsewhere othermachine \
|
https://git.rootiest.dev/rootiest/emptycase.git /gone/elsewhere othermachine >$vroot2/agent-vault/projects/$enew_slug/origin
|
||||||
>$vroot2/agent-vault/projects/$enew_slug/origin
|
|
||||||
|
|
||||||
pushd $emp >/dev/null
|
pushd $emp >/dev/null
|
||||||
set -l erc (agents-vault --silent 2>/dev/null; echo $status)
|
set -l erc (agents-vault --silent 2>/dev/null; echo $status)
|
||||||
@@ -579,7 +593,8 @@ check "removal: old remote entry removed" false (test -d $vroot2/agent-vault/pro
|
|||||||
# still find and adopt it. Before the fix, the fallback only lowercased
|
# still find and adopt it. Before the fix, the fallback only lowercased
|
||||||
# the basename instead of sanitizing it like _agents_repo_slug does, so it
|
# the basename instead of sanitizing it like _agents_repo_slug does, so it
|
||||||
# could never match the real entry directory for a name like this.
|
# could never match the real entry directory for a name like this.
|
||||||
set -l dirty_root (mktemp -d); set -ga TMPDIRS $dirty_root
|
set -l dirty_root (mktemp -d)
|
||||||
|
set -ga TMPDIRS $dirty_root
|
||||||
set -l dp "$dirty_root/My Project!"
|
set -l dp "$dirty_root/My Project!"
|
||||||
mkdir -p "$dp"
|
mkdir -p "$dp"
|
||||||
git -C "$dp" init -q
|
git -C "$dp" init -q
|
||||||
@@ -590,7 +605,7 @@ git -C "$dp" config core.hooksPath /dev/null
|
|||||||
|
|
||||||
set -l dmangled (string replace -a '/' '-' -- $dp | string replace -a '.' '-')
|
set -l dmangled (string replace -a '/' '-' -- $dp | string replace -a '.' '-')
|
||||||
mkdir -p $croot2/$dmangled/memory
|
mkdir -p $croot2/$dmangled/memory
|
||||||
echo "dirty-precious" >$croot2/$dmangled/memory/keep.md
|
echo dirty-precious >$croot2/$dmangled/memory/keep.md
|
||||||
|
|
||||||
pushd $dp >/dev/null
|
pushd $dp >/dev/null
|
||||||
agents-vault --silent
|
agents-vault --silent
|
||||||
@@ -621,7 +636,8 @@ check "fallback old entry removed" false (test -d $vroot2/agent-vault/projects/$
|
|||||||
# is not: a --silent run that did the right thing and returned 0 still
|
# is not: a --silent run that did the right thing and returned 0 still
|
||||||
# printed "rm: cannot remove ...: Is a directory", which is the only
|
# printed "rm: cannot remove ...: Is a directory", which is the only
|
||||||
# thing the user sees and reads as a failure.
|
# thing the user sees and reads as a failure.
|
||||||
set -l real_root (mktemp -d); set -ga TMPDIRS $real_root
|
set -l real_root (mktemp -d)
|
||||||
|
set -ga TMPDIRS $real_root
|
||||||
set -l rp "$real_root/proj"
|
set -l rp "$real_root/proj"
|
||||||
mkdir -p "$rp"
|
mkdir -p "$rp"
|
||||||
git -C "$rp" init -q
|
git -C "$rp" init -q
|
||||||
@@ -632,7 +648,7 @@ git -C "$rp" config core.hooksPath /dev/null
|
|||||||
|
|
||||||
set -l rmangled (string replace -a '/' '-' -- $rp | string replace -a '.' '-')
|
set -l rmangled (string replace -a '/' '-' -- $rp | string replace -a '.' '-')
|
||||||
mkdir -p $croot2/$rmangled/memory
|
mkdir -p $croot2/$rmangled/memory
|
||||||
echo "banked" >$croot2/$rmangled/memory/old.md
|
echo banked >$croot2/$rmangled/memory/old.md
|
||||||
|
|
||||||
pushd $rp >/dev/null
|
pushd $rp >/dev/null
|
||||||
agents-vault --silent
|
agents-vault --silent
|
||||||
@@ -643,10 +659,11 @@ popd >/dev/null
|
|||||||
# never seen, then change the slug so the migration runs.
|
# never seen, then change the slug so the migration runs.
|
||||||
rm -f $croot2/$rmangled/memory
|
rm -f $croot2/$rmangled/memory
|
||||||
mkdir -p $croot2/$rmangled/memory
|
mkdir -p $croot2/$rmangled/memory
|
||||||
echo "written-live" >$croot2/$rmangled/memory/fresh.md
|
echo written-live >$croot2/$rmangled/memory/fresh.md
|
||||||
git -C $rp remote add origin https://git.rootiest.dev/rootiest/realdir.git
|
git -C $rp remote add origin https://git.rootiest.dev/rootiest/realdir.git
|
||||||
|
|
||||||
set -l rerr (mktemp); set -ga TMPDIRS $rerr
|
set -l rerr (mktemp)
|
||||||
|
set -ga TMPDIRS $rerr
|
||||||
pushd $rp >/dev/null
|
pushd $rp >/dev/null
|
||||||
set -l real_rc (agents-vault --silent 2>$rerr; echo $status)
|
set -l real_rc (agents-vault --silent 2>$rerr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -673,8 +690,10 @@ echo ""
|
|||||||
echo "== agents-vault (a real git clone) =="
|
echo "== agents-vault (a real git clone) =="
|
||||||
|
|
||||||
# Machine A: populate a vault and let agents-vault commit it.
|
# Machine A: populate a vault and let agents-vault commit it.
|
||||||
set -l cl_vroot (mktemp -d); set -ga TMPDIRS $cl_vroot
|
set -l cl_vroot (mktemp -d)
|
||||||
set -l cl_croot (mktemp -d); set -ga TMPDIRS $cl_croot
|
set -ga TMPDIRS $cl_vroot
|
||||||
|
set -l cl_croot (mktemp -d)
|
||||||
|
set -ga TMPDIRS $cl_croot
|
||||||
set -g __fish_agent_vault_dir $cl_vroot/agent-vault
|
set -g __fish_agent_vault_dir $cl_vroot/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $cl_croot
|
set -g __fish_agent_vault_claude_root $cl_croot
|
||||||
|
|
||||||
@@ -698,7 +717,8 @@ agents-vault --silent
|
|||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
# The clone, exactly as the README tells a user to make it.
|
# The clone, exactly as the README tells a user to make it.
|
||||||
set -l cl_new (mktemp -d); set -ga TMPDIRS $cl_new
|
set -l cl_new (mktemp -d)
|
||||||
|
set -ga TMPDIRS $cl_new
|
||||||
git clone -q $cl_vroot/agent-vault $cl_new/agent-vault
|
git clone -q $cl_vroot/agent-vault $cl_new/agent-vault
|
||||||
git -C $cl_new/agent-vault config user.email t@t
|
git -C $cl_new/agent-vault config user.email t@t
|
||||||
git -C $cl_new/agent-vault config user.name t
|
git -C $cl_new/agent-vault config user.name t
|
||||||
@@ -709,7 +729,8 @@ check "clone: the empty entry has no claude/ subtree" false (test -d $cl_new/age
|
|||||||
check "clone: the empty entry is its origin file alone" true (test -f $cl_new/agent-vault/projects/$cl_empty_slug/origin; and echo true; or echo false)
|
check "clone: the empty entry is its origin file alone" true (test -f $cl_new/agent-vault/projects/$cl_empty_slug/origin; and echo true; or echo false)
|
||||||
|
|
||||||
# Machine B, case 1: an ordinary run against the clone restores memory.
|
# Machine B, case 1: an ordinary run against the clone restores memory.
|
||||||
set -l cl_croot2 (mktemp -d); set -ga TMPDIRS $cl_croot2
|
set -l cl_croot2 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $cl_croot2
|
||||||
set -g __fish_agent_vault_dir $cl_new/agent-vault
|
set -g __fish_agent_vault_dir $cl_new/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $cl_croot2
|
set -g __fish_agent_vault_claude_root $cl_croot2
|
||||||
|
|
||||||
@@ -762,10 +783,14 @@ set -e __fish_agent_vault_claude_root
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (global state) =="
|
echo "== agents-vault (global state) =="
|
||||||
|
|
||||||
set -l vroot5 (mktemp -d); set -ga TMPDIRS $vroot5
|
set -l vroot5 (mktemp -d)
|
||||||
set -l croot5 (mktemp -d); set -ga TMPDIRS $croot5
|
set -ga TMPDIRS $vroot5
|
||||||
set -l chome5 (mktemp -d); set -ga TMPDIRS $chome5
|
set -l croot5 (mktemp -d)
|
||||||
set -l agy5 (mktemp -d); set -ga TMPDIRS $agy5
|
set -ga TMPDIRS $croot5
|
||||||
|
set -l chome5 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome5
|
||||||
|
set -l agy5 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy5
|
||||||
set -g __fish_agent_vault_dir $vroot5/agent-vault
|
set -g __fish_agent_vault_dir $vroot5/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot5
|
set -g __fish_agent_vault_claude_root $croot5
|
||||||
set -g __fish_agent_vault_claude_home $chome5
|
set -g __fish_agent_vault_claude_home $chome5
|
||||||
@@ -802,7 +827,8 @@ printf 'transcript\n' >$agy5/knowledge/session.jsonl
|
|||||||
# the store boundary has to hold on its own. $outside5 stands in for that
|
# the store boundary has to hold on its own. $outside5 stands in for that
|
||||||
# home: a directory the store has no business reaching into, planted with
|
# home: a directory the store has no business reaching into, planted with
|
||||||
# exactly the shapes that qualify.
|
# exactly the shapes that qualify.
|
||||||
set -l outside5 (mktemp -d); set -ga TMPDIRS $outside5
|
set -l outside5 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $outside5
|
||||||
mkdir -p $outside5/nested
|
mkdir -p $outside5/nested
|
||||||
echo SECRET-OUTSIDE-KNOWLEDGE >$outside5/leaked.json
|
echo SECRET-OUTSIDE-KNOWLEDGE >$outside5/leaked.json
|
||||||
echo SECRET-OUTSIDE-KNOWLEDGE >$outside5/target.md
|
echo SECRET-OUTSIDE-KNOWLEDGE >$outside5/target.md
|
||||||
@@ -905,10 +931,14 @@ set -e __fish_agent_vault_claude_root
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (global emergent restore) =="
|
echo "== agents-vault (global emergent restore) =="
|
||||||
|
|
||||||
set -l vroot6 (mktemp -d); set -ga TMPDIRS $vroot6
|
set -l vroot6 (mktemp -d)
|
||||||
set -l croot6 (mktemp -d); set -ga TMPDIRS $croot6
|
set -ga TMPDIRS $vroot6
|
||||||
set -l chome6 (mktemp -d); set -ga TMPDIRS $chome6
|
set -l croot6 (mktemp -d)
|
||||||
set -l agy6 (mktemp -d); set -ga TMPDIRS $agy6
|
set -ga TMPDIRS $croot6
|
||||||
|
set -l chome6 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome6
|
||||||
|
set -l agy6 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy6
|
||||||
set -g __fish_agent_vault_dir $vroot6/agent-vault
|
set -g __fish_agent_vault_dir $vroot6/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot6
|
set -g __fish_agent_vault_claude_root $croot6
|
||||||
set -g __fish_agent_vault_claude_home $chome6
|
set -g __fish_agent_vault_claude_home $chome6
|
||||||
@@ -927,8 +957,10 @@ check "global restore: vault content readable through the link" restored-global
|
|||||||
|
|
||||||
# A home with neither side populated must not have a memory/ invented for
|
# A home with neither side populated must not have a memory/ invented for
|
||||||
# it: ~/.claude/memory does not exist by default.
|
# it: ~/.claude/memory does not exist by default.
|
||||||
set -l chome7 (mktemp -d); set -ga TMPDIRS $chome7
|
set -l chome7 (mktemp -d)
|
||||||
set -l vroot7 (mktemp -d); set -ga TMPDIRS $vroot7
|
set -ga TMPDIRS $chome7
|
||||||
|
set -l vroot7 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $vroot7
|
||||||
set -g __fish_agent_vault_dir $vroot7/agent-vault
|
set -g __fish_agent_vault_dir $vroot7/agent-vault
|
||||||
set -g __fish_agent_vault_claude_home $chome7
|
set -g __fish_agent_vault_claude_home $chome7
|
||||||
set -l gp7 (new_repo https://git.rootiest.dev/rootiest/globals-absent.git)
|
set -l gp7 (new_repo https://git.rootiest.dev/rootiest/globals-absent.git)
|
||||||
@@ -951,10 +983,14 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (global link failure is non-fatal) =="
|
echo "== agents-vault (global link failure is non-fatal) =="
|
||||||
|
|
||||||
set -l vroot8 (mktemp -d); set -ga TMPDIRS $vroot8
|
set -l vroot8 (mktemp -d)
|
||||||
set -l croot8 (mktemp -d); set -ga TMPDIRS $croot8
|
set -ga TMPDIRS $vroot8
|
||||||
set -l chome8 (mktemp -d); set -ga TMPDIRS $chome8
|
set -l croot8 (mktemp -d)
|
||||||
set -l agy8 (mktemp -d); set -ga TMPDIRS $agy8
|
set -ga TMPDIRS $croot8
|
||||||
|
set -l chome8 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome8
|
||||||
|
set -l agy8 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy8
|
||||||
set -g __fish_agent_vault_dir $vroot8/agent-vault
|
set -g __fish_agent_vault_dir $vroot8/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot8
|
set -g __fish_agent_vault_claude_root $croot8
|
||||||
set -g __fish_agent_vault_claude_home $chome8
|
set -g __fish_agent_vault_claude_home $chome8
|
||||||
@@ -972,7 +1008,8 @@ set -l fmangled (string replace -a '/' '-' -- $fp | string replace -a '.' '-')
|
|||||||
mkdir -p $croot8/$fmangled/memory
|
mkdir -p $croot8/$fmangled/memory
|
||||||
echo project-memory >$croot8/$fmangled/memory/p.md
|
echo project-memory >$croot8/$fmangled/memory/p.md
|
||||||
|
|
||||||
set -l ferr (mktemp); set -ga TMPDIRS $ferr
|
set -l ferr (mktemp)
|
||||||
|
set -ga TMPDIRS $ferr
|
||||||
pushd $fp >/dev/null
|
pushd $fp >/dev/null
|
||||||
set -l frc (agents-vault --silent 2>$ferr; echo $status)
|
set -l frc (agents-vault --silent 2>$ferr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -991,7 +1028,8 @@ check "global link failure: per-project memory is committed" true (git -C $vroot
|
|||||||
# A failed global link must not be recorded as done: the next run has to
|
# A failed global link must not be recorded as done: the next run has to
|
||||||
# re-enter the block and warn again, not treat the vault as correct.
|
# re-enter the block and warn again, not treat the vault as correct.
|
||||||
pushd $fp >/dev/null
|
pushd $fp >/dev/null
|
||||||
set -l ferr2 (mktemp); set -ga TMPDIRS $ferr2
|
set -l ferr2 (mktemp)
|
||||||
|
set -ga TMPDIRS $ferr2
|
||||||
agents-vault --silent 2>$ferr2
|
agents-vault --silent 2>$ferr2
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
check "global link failure: retried on the next run" true (string match -q "*$chome8/memory*" -- (cat $ferr2); and echo true; or echo false)
|
check "global link failure: retried on the next run" true (string match -q "*$chome8/memory*" -- (cat $ferr2); and echo true; or echo false)
|
||||||
@@ -1008,17 +1046,22 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (status, remote, adopt) =="
|
echo "== agents-vault (status, remote, adopt) =="
|
||||||
|
|
||||||
set -l vroot9 (mktemp -d); set -ga TMPDIRS $vroot9
|
set -l vroot9 (mktemp -d)
|
||||||
set -l croot9 (mktemp -d); set -ga TMPDIRS $croot9
|
set -ga TMPDIRS $vroot9
|
||||||
set -l chome9 (mktemp -d); set -ga TMPDIRS $chome9
|
set -l croot9 (mktemp -d)
|
||||||
set -l agy9 (mktemp -d); set -ga TMPDIRS $agy9
|
set -ga TMPDIRS $croot9
|
||||||
|
set -l chome9 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome9
|
||||||
|
set -l agy9 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy9
|
||||||
set -g __fish_agent_vault_dir $vroot9/agent-vault
|
set -g __fish_agent_vault_dir $vroot9/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot9
|
set -g __fish_agent_vault_claude_root $croot9
|
||||||
set -g __fish_agent_vault_claude_home $chome9
|
set -g __fish_agent_vault_claude_home $chome9
|
||||||
set -g __fish_agent_vault_agy_root $agy9
|
set -g __fish_agent_vault_agy_root $agy9
|
||||||
|
|
||||||
# A report asked for before the vault exists must say so, not scaffold one.
|
# A report asked for before the vault exists must say so, not scaffold one.
|
||||||
set -l s0out (mktemp); set -ga TMPDIRS $s0out
|
set -l s0out (mktemp)
|
||||||
|
set -ga TMPDIRS $s0out
|
||||||
set -l s0rc (agents-vault --status >$s0out; echo $status)
|
set -l s0rc (agents-vault --status >$s0out; echo $status)
|
||||||
check "status without a vault exits 0" 0 "$s0rc"
|
check "status without a vault exits 0" 0 "$s0rc"
|
||||||
check "status without a vault says so" true (string match -q '*no vault*' -- (cat $s0out); and echo true; or echo false)
|
check "status without a vault says so" true (string match -q '*no vault*' -- (cat $s0out); and echo true; or echo false)
|
||||||
@@ -1068,7 +1111,8 @@ check "status reports the remote" true (string match -q '*rootiest/agent-vault.g
|
|||||||
# A *failed* remote update must return non-zero. Reporting success after a
|
# A *failed* remote update must return non-zero. Reporting success after a
|
||||||
# git command that did not run is the same silent-false-success shape that
|
# git command that did not run is the same silent-false-success shape that
|
||||||
# a hook-rejected commit produced earlier in this project.
|
# a hook-rejected commit produced earlier in this project.
|
||||||
set -l rerr (mktemp); set -ga TMPDIRS $rerr
|
set -l rerr (mktemp)
|
||||||
|
set -ga TMPDIRS $rerr
|
||||||
set -l rshim (failing_shim git set-url)
|
set -l rshim (failing_shim git set-url)
|
||||||
set -l rpath $PATH
|
set -l rpath $PATH
|
||||||
set PATH $rshim $PATH
|
set PATH $rshim $PATH
|
||||||
@@ -1106,10 +1150,11 @@ agents-vault --silent
|
|||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
set -l projects_before (command ls -A $vroot9/agent-vault/projects | sort | string join ',')
|
set -l projects_before (command ls -A $vroot9/agent-vault/projects | sort | string join ',')
|
||||||
|
|
||||||
set -l bad_slugs ../escape has/slash . .. 'UPPER' 'sp ace' ''
|
set -l bad_slugs ../escape has/slash . .. UPPER 'sp ace' ''
|
||||||
pushd $bp >/dev/null
|
pushd $bp >/dev/null
|
||||||
for bad in $bad_slugs
|
for bad in $bad_slugs
|
||||||
set -l berr (mktemp); set -ga TMPDIRS $berr
|
set -l berr (mktemp)
|
||||||
|
set -ga TMPDIRS $berr
|
||||||
set -l brc (agents-vault --adopt=$bad --silent 2>$berr; echo $status)
|
set -l brc (agents-vault --adopt=$bad --silent 2>$berr; echo $status)
|
||||||
check "adopt refuses '$bad'" 1 "$brc"
|
check "adopt refuses '$bad'" 1 "$brc"
|
||||||
check "adopt refuses '$bad' out loud" true (string match -q '*invalid*' -- (cat $berr); and echo true; or echo false)
|
check "adopt refuses '$bad' out loud" true (string match -q '*invalid*' -- (cat $berr); and echo true; or echo false)
|
||||||
@@ -1150,7 +1195,8 @@ set -l pre_head (git -C $vroot9/agent-vault rev-list --count HEAD)
|
|||||||
set -l pre_porcelain (git -C $vroot9/agent-vault status --porcelain | string join ',')
|
set -l pre_porcelain (git -C $vroot9/agent-vault status --porcelain | string join ',')
|
||||||
set -l pre_link (path resolve $croot9/$tmang/memory)
|
set -l pre_link (path resolve $croot9/$tmang/memory)
|
||||||
|
|
||||||
set -l terr (mktemp); set -ga TMPDIRS $terr
|
set -l terr (mktemp)
|
||||||
|
set -ga TMPDIRS $terr
|
||||||
set -l tshim (failing_shim rm $tmang)
|
set -l tshim (failing_shim rm $tmang)
|
||||||
set -l tpath $PATH
|
set -l tpath $PATH
|
||||||
set PATH $tshim $PATH
|
set PATH $tshim $PATH
|
||||||
@@ -1203,8 +1249,7 @@ popd >/dev/null
|
|||||||
# The target must be *tracked* for this to bite: stashing it takes files
|
# The target must be *tracked* for this to bite: stashing it takes files
|
||||||
# git knows about out from under the index, which is the whole hazard.
|
# git knows about out from under the index, which is the whole hazard.
|
||||||
mkdir -p $vroot9/agent-vault/projects/stash-target/claude/memory
|
mkdir -p $vroot9/agent-vault/projects/stash-target/claude/memory
|
||||||
printf 'remote: (none)\npath: %s\nhost: t\n' /nowhere \
|
printf 'remote: (none)\npath: %s\nhost: t\n' /nowhere >$vroot9/agent-vault/projects/stash-target/origin
|
||||||
>$vroot9/agent-vault/projects/stash-target/origin
|
|
||||||
pushd $sa >/dev/null
|
pushd $sa >/dev/null
|
||||||
agents-vault --silent
|
agents-vault --silent
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1245,8 +1290,7 @@ agents-vault --silent
|
|||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
mkdir -p $vroot9/agent-vault/projects/stashfail-target/claude/memory
|
mkdir -p $vroot9/agent-vault/projects/stashfail-target/claude/memory
|
||||||
printf 'remote: (none)\npath: %s\nhost: t\n' /nowhere-else \
|
printf 'remote: (none)\npath: %s\nhost: t\n' /nowhere-else >$vroot9/agent-vault/projects/stashfail-target/origin
|
||||||
>$vroot9/agent-vault/projects/stashfail-target/origin
|
|
||||||
pushd $sf >/dev/null
|
pushd $sf >/dev/null
|
||||||
agents-vault --silent
|
agents-vault --silent
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1257,7 +1301,8 @@ set -l sf_porcelain (git -C $vroot9/agent-vault status --porcelain | string join
|
|||||||
set -l sf_link (path resolve $croot9/$sfmang/memory)
|
set -l sf_link (path resolve $croot9/$sfmang/memory)
|
||||||
check "stash adopt rollback: vault clean before the adopt" "" "$sf_porcelain"
|
check "stash adopt rollback: vault clean before the adopt" "" "$sf_porcelain"
|
||||||
|
|
||||||
set -l sferr (mktemp); set -ga TMPDIRS $sferr
|
set -l sferr (mktemp)
|
||||||
|
set -ga TMPDIRS $sferr
|
||||||
set -l sfshim (failing_shim rm $sfmang)
|
set -l sfshim (failing_shim rm $sfmang)
|
||||||
set -l sfpath $PATH
|
set -l sfpath $PATH
|
||||||
set PATH $sfshim $PATH
|
set PATH $sfshim $PATH
|
||||||
@@ -1359,10 +1404,14 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (restore) =="
|
echo "== agents-vault (restore) =="
|
||||||
|
|
||||||
set -l vroot10 (mktemp -d); set -ga TMPDIRS $vroot10
|
set -l vroot10 (mktemp -d)
|
||||||
set -l croot10 (mktemp -d); set -ga TMPDIRS $croot10
|
set -ga TMPDIRS $vroot10
|
||||||
set -l chome10 (mktemp -d); set -ga TMPDIRS $chome10
|
set -l croot10 (mktemp -d)
|
||||||
set -l agy10 (mktemp -d); set -ga TMPDIRS $agy10
|
set -ga TMPDIRS $croot10
|
||||||
|
set -l chome10 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome10
|
||||||
|
set -l agy10 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy10
|
||||||
set -g __fish_agent_vault_dir $vroot10/agent-vault
|
set -g __fish_agent_vault_dir $vroot10/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot10
|
set -g __fish_agent_vault_claude_root $croot10
|
||||||
set -g __fish_agent_vault_claude_home $chome10
|
set -g __fish_agent_vault_claude_home $chome10
|
||||||
@@ -1389,9 +1438,9 @@ check "restore: names what it restored" true (string match -q '*restoreme*' -- "
|
|||||||
# succeeds and says which entry needs --adopt.
|
# succeeds and says which entry needs --adopt.
|
||||||
mkdir -p $vroot10/agent-vault/projects/ghost-entry/claude/memory
|
mkdir -p $vroot10/agent-vault/projects/ghost-entry/claude/memory
|
||||||
echo x >$vroot10/agent-vault/projects/ghost-entry/claude/memory/x.md
|
echo x >$vroot10/agent-vault/projects/ghost-entry/claude/memory/x.md
|
||||||
printf 'remote: (none)\npath: %s\nhost: t\n' $vroot10/gone-forever \
|
printf 'remote: (none)\npath: %s\nhost: t\n' $vroot10/gone-forever >$vroot10/agent-vault/projects/ghost-entry/origin
|
||||||
>$vroot10/agent-vault/projects/ghost-entry/origin
|
set -l r2out (mktemp)
|
||||||
set -l r2out (mktemp); set -ga TMPDIRS $r2out
|
set -ga TMPDIRS $r2out
|
||||||
set -l r2rc (agents-vault --restore >$r2out; echo $status)
|
set -l r2rc (agents-vault --restore >$r2out; echo $status)
|
||||||
check "restore: exits 0 with an unplaceable entry" 0 "$r2rc"
|
check "restore: exits 0 with an unplaceable entry" 0 "$r2rc"
|
||||||
check "restore: reports the unplaceable entry" true (string match -q '*ghost-entry*' -- (cat $r2out); and echo true; or echo false)
|
check "restore: reports the unplaceable entry" true (string match -q '*ghost-entry*' -- (cat $r2out); and echo true; or echo false)
|
||||||
@@ -1406,8 +1455,7 @@ set -l dotp (new_repo https://git.rootiest.dev/rootiest/dotted.git)
|
|||||||
set -l dotmang (string replace -a '/' '-' -- $dotp | string replace -a '.' '-')
|
set -l dotmang (string replace -a '/' '-' -- $dotp | string replace -a '.' '-')
|
||||||
mkdir -p $vroot10/agent-vault/projects/.dot-entry/claude/memory
|
mkdir -p $vroot10/agent-vault/projects/.dot-entry/claude/memory
|
||||||
echo dot-precious >$vroot10/agent-vault/projects/.dot-entry/claude/memory/keep.md
|
echo dot-precious >$vroot10/agent-vault/projects/.dot-entry/claude/memory/keep.md
|
||||||
printf 'remote: (none)\npath: %s\nhost: t\n' $dotp \
|
printf 'remote: (none)\npath: %s\nhost: t\n' $dotp >$vroot10/agent-vault/projects/.dot-entry/origin
|
||||||
>$vroot10/agent-vault/projects/.dot-entry/origin
|
|
||||||
check "dot-led entry: a glob really does skip it" false (string match -q '*.dot-entry*' -- (echo $vroot10/agent-vault/projects/*); and echo true; or echo false)
|
check "dot-led entry: a glob really does skip it" false (string match -q '*.dot-entry*' -- (echo $vroot10/agent-vault/projects/*); and echo true; or echo false)
|
||||||
|
|
||||||
set -l dotreport (agents-vault --status)
|
set -l dotreport (agents-vault --status)
|
||||||
@@ -1425,7 +1473,8 @@ rm -f $croot10/$rmang/memory
|
|||||||
set -l r3shim (failing_shim ln $rmang)
|
set -l r3shim (failing_shim ln $rmang)
|
||||||
set -l r3path $PATH
|
set -l r3path $PATH
|
||||||
set PATH $r3shim $PATH
|
set PATH $r3shim $PATH
|
||||||
set -l r3err (mktemp); set -ga TMPDIRS $r3err
|
set -l r3err (mktemp)
|
||||||
|
set -ga TMPDIRS $r3err
|
||||||
set -l r3rc (agents-vault --restore >/dev/null 2>$r3err; echo $status)
|
set -l r3rc (agents-vault --restore >/dev/null 2>$r3err; echo $status)
|
||||||
set PATH $r3path
|
set PATH $r3path
|
||||||
check "restore: a failed relink returns non-zero" 1 "$r3rc"
|
check "restore: a failed relink returns non-zero" 1 "$r3rc"
|
||||||
@@ -1440,11 +1489,16 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (push) =="
|
echo "== agents-vault (push) =="
|
||||||
|
|
||||||
set -l vroot11 (mktemp -d); set -ga TMPDIRS $vroot11
|
set -l vroot11 (mktemp -d)
|
||||||
set -l croot11 (mktemp -d); set -ga TMPDIRS $croot11
|
set -ga TMPDIRS $vroot11
|
||||||
set -l chome11 (mktemp -d); set -ga TMPDIRS $chome11
|
set -l croot11 (mktemp -d)
|
||||||
set -l agy11 (mktemp -d); set -ga TMPDIRS $agy11
|
set -ga TMPDIRS $croot11
|
||||||
set -l bare (mktemp -d); set -ga TMPDIRS $bare
|
set -l chome11 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome11
|
||||||
|
set -l agy11 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy11
|
||||||
|
set -l bare (mktemp -d)
|
||||||
|
set -ga TMPDIRS $bare
|
||||||
git init -q --bare $bare
|
git init -q --bare $bare
|
||||||
set -g __fish_agent_vault_dir $vroot11/agent-vault
|
set -g __fish_agent_vault_dir $vroot11/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot11
|
set -g __fish_agent_vault_claude_root $croot11
|
||||||
@@ -1459,7 +1513,8 @@ echo pushed >$croot11/$pmang/memory/p.md
|
|||||||
|
|
||||||
# --push with no remote must fail loudly. The commit still happened, so
|
# --push with no remote must fail loudly. The commit still happened, so
|
||||||
# silently returning 0 would read as "backed up off this machine".
|
# silently returning 0 would read as "backed up off this machine".
|
||||||
set -l perr (mktemp); set -ga TMPDIRS $perr
|
set -l perr (mktemp)
|
||||||
|
set -ga TMPDIRS $perr
|
||||||
pushd $pp >/dev/null
|
pushd $pp >/dev/null
|
||||||
set -l prc0 (agents-vault --push --silent 2>$perr; echo $status)
|
set -l prc0 (agents-vault --push --silent 2>$perr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1498,7 +1553,8 @@ check "autopush pushes when enabled" pushed-auto (git -C $bare show $vbranch:pro
|
|||||||
set -l deadremote $vroot11/not-a-repo.git
|
set -l deadremote $vroot11/not-a-repo.git
|
||||||
agents-vault --remote=$deadremote --silent
|
agents-vault --remote=$deadremote --silent
|
||||||
echo pushed-never >$croot11/$pmang/memory/p4.md
|
echo pushed-never >$croot11/$pmang/memory/p4.md
|
||||||
set -l fperr (mktemp); set -ga TMPDIRS $fperr
|
set -l fperr (mktemp)
|
||||||
|
set -ga TMPDIRS $fperr
|
||||||
pushd $pp >/dev/null
|
pushd $pp >/dev/null
|
||||||
set -l fprc (agents-vault --push --silent 2>$fperr; echo $status)
|
set -l fprc (agents-vault --push --silent 2>$fperr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1510,7 +1566,8 @@ check "failing push still committed locally" true (git -C $vroot11/agent-vault l
|
|||||||
# saying "Synced" must not come with a zero exit when the push failed.
|
# saying "Synced" must not come with a zero exit when the push failed.
|
||||||
echo pushed-never-2 >$croot11/$pmang/memory/p5.md
|
echo pushed-never-2 >$croot11/$pmang/memory/p5.md
|
||||||
set -g __fish_agent_vault_autopush 1
|
set -g __fish_agent_vault_autopush 1
|
||||||
set -l fp2err (mktemp); set -ga TMPDIRS $fp2err
|
set -l fp2err (mktemp)
|
||||||
|
set -ga TMPDIRS $fp2err
|
||||||
pushd $pp >/dev/null
|
pushd $pp >/dev/null
|
||||||
set -l fp2rc (agents-vault --quiet 2>$fp2err >/dev/null; echo $status)
|
set -l fp2rc (agents-vault --quiet 2>$fp2err >/dev/null; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1561,10 +1618,14 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (the user's ssh command wins) =="
|
echo "== agents-vault (the user's ssh command wins) =="
|
||||||
|
|
||||||
set -l vroot14 (mktemp -d); set -ga TMPDIRS $vroot14
|
set -l vroot14 (mktemp -d)
|
||||||
set -l croot14 (mktemp -d); set -ga TMPDIRS $croot14
|
set -ga TMPDIRS $vroot14
|
||||||
set -l chome14 (mktemp -d); set -ga TMPDIRS $chome14
|
set -l croot14 (mktemp -d)
|
||||||
set -l agy14 (mktemp -d); set -ga TMPDIRS $agy14
|
set -ga TMPDIRS $croot14
|
||||||
|
set -l chome14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome14
|
||||||
|
set -l agy14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy14
|
||||||
set -g __fish_agent_vault_dir $vroot14/agent-vault
|
set -g __fish_agent_vault_dir $vroot14/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot14
|
set -g __fish_agent_vault_claude_root $croot14
|
||||||
set -g __fish_agent_vault_claude_home $chome14
|
set -g __fish_agent_vault_claude_home $chome14
|
||||||
@@ -1574,7 +1635,8 @@ set -g __fish_agent_vault_agy_root $agy14
|
|||||||
# plain `ssh` (what the injected default resolves to) and one named
|
# plain `ssh` (what the injected default resolves to) and one named
|
||||||
# explicitly by the user. Both refuse the connection, so nothing leaves
|
# explicitly by the user. Both refuse the connection, so nothing leaves
|
||||||
# the machine and no test waits on a network.
|
# the machine and no test waits on a network.
|
||||||
set -l sbin (mktemp -d); set -ga TMPDIRS $sbin
|
set -l sbin (mktemp -d)
|
||||||
|
set -ga TMPDIRS $sbin
|
||||||
set -l pathssh_log $sbin/path-ssh.log
|
set -l pathssh_log $sbin/path-ssh.log
|
||||||
set -l usessh $sbin/user-ssh
|
set -l usessh $sbin/user-ssh
|
||||||
set -l usessh_log $sbin/user-ssh.log
|
set -l usessh_log $sbin/user-ssh.log
|
||||||
@@ -1655,11 +1717,16 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (autopush without timeout) =="
|
echo "== agents-vault (autopush without timeout) =="
|
||||||
|
|
||||||
set -l vroot15 (mktemp -d); set -ga TMPDIRS $vroot15
|
set -l vroot15 (mktemp -d)
|
||||||
set -l croot15 (mktemp -d); set -ga TMPDIRS $croot15
|
set -ga TMPDIRS $vroot15
|
||||||
set -l chome15 (mktemp -d); set -ga TMPDIRS $chome15
|
set -l croot15 (mktemp -d)
|
||||||
set -l agy15 (mktemp -d); set -ga TMPDIRS $agy15
|
set -ga TMPDIRS $croot15
|
||||||
set -l bare15 (mktemp -d); set -ga TMPDIRS $bare15
|
set -l chome15 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome15
|
||||||
|
set -l agy15 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy15
|
||||||
|
set -l bare15 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $bare15
|
||||||
git init -q --bare $bare15
|
git init -q --bare $bare15
|
||||||
set -g __fish_agent_vault_dir $vroot15/agent-vault
|
set -g __fish_agent_vault_dir $vroot15/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot15
|
set -g __fish_agent_vault_claude_root $croot15
|
||||||
@@ -1678,7 +1745,8 @@ popd >/dev/null
|
|||||||
# rev-parse would report the literal string HEAD with a fatal on stderr.
|
# rev-parse would report the literal string HEAD with a fatal on stderr.
|
||||||
set -l vb15 (git -C $vroot15/agent-vault symbolic-ref --short HEAD)
|
set -l vb15 (git -C $vroot15/agent-vault symbolic-ref --short HEAD)
|
||||||
|
|
||||||
set -l shimroot (mktemp -d); set -ga TMPDIRS $shimroot
|
set -l shimroot (mktemp -d)
|
||||||
|
set -ga TMPDIRS $shimroot
|
||||||
set -l nopath
|
set -l nopath
|
||||||
set -l shimn 0
|
set -l shimn 0
|
||||||
for d in $PATH
|
for d in $PATH
|
||||||
@@ -1696,7 +1764,8 @@ end
|
|||||||
set -l realpath15 $PATH
|
set -l realpath15 $PATH
|
||||||
|
|
||||||
echo t2 >$croot15/$tmang15/memory/t2.md
|
echo t2 >$croot15/$tmang15/memory/t2.md
|
||||||
set -l terr (mktemp); set -ga TMPDIRS $terr
|
set -l terr (mktemp)
|
||||||
|
set -ga TMPDIRS $terr
|
||||||
set -g __fish_agent_vault_autopush 1
|
set -g __fish_agent_vault_autopush 1
|
||||||
set -g PATH $nopath
|
set -g PATH $nopath
|
||||||
check "the shimmed PATH really has no timeout" false (type -q timeout; and echo true; or echo false)
|
check "the shimmed PATH really has no timeout" false (type -q timeout; and echo true; or echo false)
|
||||||
@@ -1737,10 +1806,14 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (a failed vault commit is fatal) =="
|
echo "== agents-vault (a failed vault commit is fatal) =="
|
||||||
|
|
||||||
set -l vroot13 (mktemp -d); set -ga TMPDIRS $vroot13
|
set -l vroot13 (mktemp -d)
|
||||||
set -l croot13 (mktemp -d); set -ga TMPDIRS $croot13
|
set -ga TMPDIRS $vroot13
|
||||||
set -l chome13 (mktemp -d); set -ga TMPDIRS $chome13
|
set -l croot13 (mktemp -d)
|
||||||
set -l agy13 (mktemp -d); set -ga TMPDIRS $agy13
|
set -ga TMPDIRS $croot13
|
||||||
|
set -l chome13 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome13
|
||||||
|
set -l agy13 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy13
|
||||||
set -g __fish_agent_vault_dir $vroot13/agent-vault
|
set -g __fish_agent_vault_dir $vroot13/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot13
|
set -g __fish_agent_vault_claude_root $croot13
|
||||||
set -g __fish_agent_vault_claude_home $chome13
|
set -g __fish_agent_vault_claude_home $chome13
|
||||||
@@ -1763,7 +1836,8 @@ printf '#!/bin/sh\nexit 1\n' >$vroot13/agent-vault/.agents-tools/hooks/pre-commi
|
|||||||
chmod +x $vroot13/agent-vault/.agents-tools/hooks/pre-commit
|
chmod +x $vroot13/agent-vault/.agents-tools/hooks/pre-commit
|
||||||
echo hook-more >$croot13/$hmang13/memory/keep2.md
|
echo hook-more >$croot13/$hmang13/memory/keep2.md
|
||||||
set -l hhead13 (git -C $vroot13/agent-vault rev-list --count HEAD)
|
set -l hhead13 (git -C $vroot13/agent-vault rev-list --count HEAD)
|
||||||
set -l herr13 (mktemp); set -ga TMPDIRS $herr13
|
set -l herr13 (mktemp)
|
||||||
|
set -ga TMPDIRS $herr13
|
||||||
pushd $hp13 >/dev/null
|
pushd $hp13 >/dev/null
|
||||||
set -l hrc13 (agents-vault --silent 2>$herr13; echo $status)
|
set -l hrc13 (agents-vault --silent 2>$herr13; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1782,16 +1856,21 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
# moves ahead (or goes out of reach) is not a backup; --push is where the
|
# moves ahead (or goes out of reach) is not a backup; --push is where the
|
||||||
# divergence has to be reckoned with, and where the two ways it can fail
|
# divergence has to be reckoned with, and where the two ways it can fail
|
||||||
# have to be told apart.
|
# have to be told apart.
|
||||||
set -l vroot14 (mktemp -d); set -ga TMPDIRS $vroot14
|
set -l vroot14 (mktemp -d)
|
||||||
set -l croot14 (mktemp -d); set -ga TMPDIRS $croot14
|
set -ga TMPDIRS $vroot14
|
||||||
set -l chome14 (mktemp -d); set -ga TMPDIRS $chome14
|
set -l croot14 (mktemp -d)
|
||||||
set -l agy14 (mktemp -d); set -ga TMPDIRS $agy14
|
set -ga TMPDIRS $croot14
|
||||||
|
set -l chome14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome14
|
||||||
|
set -l agy14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy14
|
||||||
set -g __fish_agent_vault_dir $vroot14/agent-vault
|
set -g __fish_agent_vault_dir $vroot14/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot14
|
set -g __fish_agent_vault_claude_root $croot14
|
||||||
set -g __fish_agent_vault_claude_home $chome14
|
set -g __fish_agent_vault_claude_home $chome14
|
||||||
set -g __fish_agent_vault_agy_root $agy14
|
set -g __fish_agent_vault_agy_root $agy14
|
||||||
|
|
||||||
set -l bare14 (mktemp -d); set -ga TMPDIRS $bare14
|
set -l bare14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $bare14
|
||||||
git init -q --bare $bare14
|
git init -q --bare $bare14
|
||||||
|
|
||||||
set -l cp14 (new_repo https://git.rootiest.dev/rootiest/conflict.git)
|
set -l cp14 (new_repo https://git.rootiest.dev/rootiest/conflict.git)
|
||||||
@@ -1806,7 +1885,8 @@ agents-vault --remote=$bare14 --silent
|
|||||||
git -C $vroot14/agent-vault push -q -u origin HEAD:refs/heads/main
|
git -C $vroot14/agent-vault push -q -u origin HEAD:refs/heads/main
|
||||||
|
|
||||||
# Another machine records a conflicting change to the same line...
|
# Another machine records a conflicting change to the same line...
|
||||||
set -l cclone14 (mktemp -d); set -ga TMPDIRS $cclone14
|
set -l cclone14 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $cclone14
|
||||||
git clone -q $bare14 $cclone14
|
git clone -q $bare14 $cclone14
|
||||||
git -C $cclone14 config user.email t@t
|
git -C $cclone14 config user.email t@t
|
||||||
git -C $cclone14 config user.name t
|
git -C $cclone14 config user.name t
|
||||||
@@ -1819,7 +1899,8 @@ git -C $cclone14 push -q origin HEAD:main
|
|||||||
# ... while this one writes conflicting memory of its own on the same
|
# ... while this one writes conflicting memory of its own on the same
|
||||||
# line. The ordinary run has to commit it: it never fetches, so the
|
# line. The ordinary run has to commit it: it never fetches, so the
|
||||||
# divergence is invisible to it and irrelevant.
|
# divergence is invisible to it and irrelevant.
|
||||||
set -l cerr14 (mktemp); set -ga TMPDIRS $cerr14
|
set -l cerr14 (mktemp)
|
||||||
|
set -ga TMPDIRS $cerr14
|
||||||
echo ours >$croot14/$cmang14/memory/keep.md
|
echo ours >$croot14/$cmang14/memory/keep.md
|
||||||
set -l chead14 (git -C $vroot14/agent-vault rev-list --count HEAD)
|
set -l chead14 (git -C $vroot14/agent-vault rev-list --count HEAD)
|
||||||
pushd $cp14 >/dev/null
|
pushd $cp14 >/dev/null
|
||||||
@@ -1885,10 +1966,14 @@ set -g __fish_agent_vault_agy_root $HERMETIC_HOME/agy
|
|||||||
echo ""
|
echo ""
|
||||||
echo "== agents-vault (dangling global memory link) =="
|
echo "== agents-vault (dangling global memory link) =="
|
||||||
|
|
||||||
set -l vroot12 (mktemp -d); set -ga TMPDIRS $vroot12
|
set -l vroot12 (mktemp -d)
|
||||||
set -l croot12 (mktemp -d); set -ga TMPDIRS $croot12
|
set -ga TMPDIRS $vroot12
|
||||||
set -l chome12 (mktemp -d); set -ga TMPDIRS $chome12
|
set -l croot12 (mktemp -d)
|
||||||
set -l agy12 (mktemp -d); set -ga TMPDIRS $agy12
|
set -ga TMPDIRS $croot12
|
||||||
|
set -l chome12 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $chome12
|
||||||
|
set -l agy12 (mktemp -d)
|
||||||
|
set -ga TMPDIRS $agy12
|
||||||
set -g __fish_agent_vault_dir $vroot12/agent-vault
|
set -g __fish_agent_vault_dir $vroot12/agent-vault
|
||||||
set -g __fish_agent_vault_claude_root $croot12
|
set -g __fish_agent_vault_claude_root $croot12
|
||||||
set -g __fish_agent_vault_claude_home $chome12
|
set -g __fish_agent_vault_claude_home $chome12
|
||||||
@@ -1900,7 +1985,8 @@ check "dangling: -L is the only signal" true (test -L $chome12/memory; and echo
|
|||||||
check "dangling: the vault side is empty" false (test -e $vroot12/agent-vault/global/claude/memory; and echo true; or echo false)
|
check "dangling: the vault side is empty" false (test -e $vroot12/agent-vault/global/claude/memory; and echo true; or echo false)
|
||||||
|
|
||||||
set -l dp (new_repo https://git.rootiest.dev/rootiest/dangling.git)
|
set -l dp (new_repo https://git.rootiest.dev/rootiest/dangling.git)
|
||||||
set -l derr (mktemp); set -ga TMPDIRS $derr
|
set -l derr (mktemp)
|
||||||
|
set -ga TMPDIRS $derr
|
||||||
pushd $dp >/dev/null
|
pushd $dp >/dev/null
|
||||||
set -l drc (agents-vault --silent 2>$derr; echo $status)
|
set -l drc (agents-vault --silent 2>$derr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
@@ -1934,7 +2020,8 @@ check "agents-init: committed the AGENTS repo" true (test (git -C $ip/AGENTS rev
|
|||||||
# Offline. The pull that used to run here blocked the launch until the
|
# Offline. The pull that used to run here blocked the launch until the
|
||||||
# remote timed out and then took the commit down with it, so an agent's
|
# remote timed out and then took the commit down with it, so an agent's
|
||||||
# edits went unrecorded on every launch away from the network.
|
# edits went unrecorded on every launch away from the network.
|
||||||
set -l ibare (mktemp -d); set -ga TMPDIRS $ibare
|
set -l ibare (mktemp -d)
|
||||||
|
set -ga TMPDIRS $ibare
|
||||||
git init -q --bare $ibare
|
git init -q --bare $ibare
|
||||||
git -C $ip/AGENTS remote add origin $ibare
|
git -C $ip/AGENTS remote add origin $ibare
|
||||||
git -C $ip/AGENTS push -q -u origin HEAD 2>/dev/null
|
git -C $ip/AGENTS push -q -u origin HEAD 2>/dev/null
|
||||||
@@ -1957,7 +2044,8 @@ printf '#!/bin/sh\nexit 1\n' >$ip/AGENTS/.agents-tools/hooks/pre-commit
|
|||||||
chmod +x $ip/AGENTS/.agents-tools/hooks/pre-commit
|
chmod +x $ip/AGENTS/.agents-tools/hooks/pre-commit
|
||||||
echo blocked >$ip/AGENTS/devlogs/blocked.md
|
echo blocked >$ip/AGENTS/devlogs/blocked.md
|
||||||
set -l ibhead (git -C $ip/AGENTS rev-list --count HEAD)
|
set -l ibhead (git -C $ip/AGENTS rev-list --count HEAD)
|
||||||
set -l ierr (mktemp); set -ga TMPDIRS $ierr
|
set -l ierr (mktemp)
|
||||||
|
set -ga TMPDIRS $ierr
|
||||||
pushd $ip >/dev/null
|
pushd $ip >/dev/null
|
||||||
set -l ibrc (agents-init --silent 2>$ierr; echo $status)
|
set -l ibrc (agents-init --silent 2>$ierr; echo $status)
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|||||||
@@ -0,0 +1,357 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# MODE: isolated
|
||||||
|
|
||||||
|
source (realpath (dirname (status filename)))/lib.fish
|
||||||
|
set -p fish_function_path $repo_root/functions
|
||||||
|
set -gx TERM xterm-256color
|
||||||
|
set -g __fish_config_dir $repo_root 2>/dev/null; or true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 1. sponge_filter_secrets: Credential Leakage Prevention
|
||||||
|
# =============================================================================
|
||||||
|
section sponge_filter_secrets
|
||||||
|
|
||||||
|
# Export test variables matching sensitive heuristics
|
||||||
|
set -gx MY_AWS_SECRET_ACCESS_KEY wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
||||||
|
set -gx GITHUB_TOKEN ghp_0123456789abcdefghijklmnopqrstuv
|
||||||
|
set -gx KOPIA_PASSWORD MyLongPassword123
|
||||||
|
|
||||||
|
# Secret appears in command -> returns 0 (filtered from history)
|
||||||
|
sponge_filter_secrets "aws s3 cp file.txt s3://bucket/ --key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||||
|
check "aws secret in command filtered" 0 $status
|
||||||
|
|
||||||
|
sponge_filter_secrets "curl -H 'Authorization: token ghp_0123456789abcdefghijklmnopqrstuv' https://api.github.com"
|
||||||
|
check "github token in command filtered" 0 $status
|
||||||
|
|
||||||
|
sponge_filter_secrets "kopia repository connect --password MyLongPassword123"
|
||||||
|
check "kopia password in command filtered" 0 $status
|
||||||
|
|
||||||
|
# Clean command with no secret -> returns 1 (kept in history)
|
||||||
|
sponge_filter_secrets "git status -s"
|
||||||
|
check "clean command retained" 1 $status
|
||||||
|
|
||||||
|
sponge_filter_secrets "echo 'Hello World'"
|
||||||
|
check "ordinary echo retained" 1 $status
|
||||||
|
|
||||||
|
# Short secret (<= 8 chars) -> ignored, returns 1 (kept)
|
||||||
|
set -gx SHORT_API_KEY 12345678
|
||||||
|
set -gx SHORT_SECRET short
|
||||||
|
sponge_filter_secrets "curl -H 'X-Key: 12345678' https://example.com"
|
||||||
|
check "8-char secret ignored" 1 $status
|
||||||
|
|
||||||
|
sponge_filter_secrets "echo short"
|
||||||
|
check "short secret ignored" 1 $status
|
||||||
|
|
||||||
|
# Path-like secret (starts with / or ~) -> ignored, returns 1 (kept)
|
||||||
|
set -gx SECRET_FILE_PATH "/home/rootiest/.token"
|
||||||
|
set -gx HOME_SECRET_PATH "~/secrets/token.txt"
|
||||||
|
sponge_filter_secrets "cat /home/rootiest/.token"
|
||||||
|
check "path-like secret starting with / ignored" 1 $status
|
||||||
|
|
||||||
|
sponge_filter_secrets "source ~/secrets/token.txt"
|
||||||
|
check "path-like secret starting with ~ ignored" 1 $status
|
||||||
|
|
||||||
|
# Empty or unset variable -> handled without error, returns 1
|
||||||
|
set -gx DUMMY_API_KEY ""
|
||||||
|
sponge_filter_secrets "echo normal command"
|
||||||
|
check "empty sensitive var does not cause crash or false filter" 1 $status
|
||||||
|
|
||||||
|
# Clean up exported test variables
|
||||||
|
set -e MY_AWS_SECRET_ACCESS_KEY
|
||||||
|
set -e GITHUB_TOKEN
|
||||||
|
set -e KOPIA_PASSWORD
|
||||||
|
set -e SHORT_API_KEY
|
||||||
|
set -e SHORT_SECRET
|
||||||
|
set -e SECRET_FILE_PATH
|
||||||
|
set -e HOME_SECRET_PATH
|
||||||
|
set -e DUMMY_API_KEY
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 2. _fish_mkdir_p: Directory Creation with Feedback
|
||||||
|
# =============================================================================
|
||||||
|
section _fish_mkdir_p
|
||||||
|
|
||||||
|
set -l mkdir_sandbox (mktemp -d)
|
||||||
|
|
||||||
|
# Empty argument returns status 1
|
||||||
|
_fish_mkdir_p
|
||||||
|
check "empty argument returns 1" 1 $status
|
||||||
|
|
||||||
|
# Existing directory returns status 0
|
||||||
|
_fish_mkdir_p $mkdir_sandbox
|
||||||
|
check "existing directory returns 0" 0 $status
|
||||||
|
|
||||||
|
# --path: creates directory and prints single line
|
||||||
|
set -l out_path (_fish_mkdir_p --path $mkdir_sandbox/dirA/dirB)
|
||||||
|
check "--path returns 0" 0 $status
|
||||||
|
check "--path creates directory" true (test -d $mkdir_sandbox/dirA/dirB; and echo true; or echo false)
|
||||||
|
check "--path outputs 'Created directory:'" true (string match -q "*Created directory:*" -- "$out_path"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# --silent: creates directory and suppresses stdout
|
||||||
|
set -l out_silent (_fish_mkdir_p --silent $mkdir_sandbox/dirC/dirD)
|
||||||
|
check "--silent returns 0" 0 $status
|
||||||
|
check "--silent creates directory" true (test -d $mkdir_sandbox/dirC/dirD; and echo true; or echo false)
|
||||||
|
check "--silent produces empty stdout" "" "$out_silent"
|
||||||
|
|
||||||
|
# -s short flag
|
||||||
|
set -l out_silent_short (_fish_mkdir_p -s $mkdir_sandbox/dirC/dirD/sub)
|
||||||
|
check "-s returns 0" 0 $status
|
||||||
|
check "-s creates directory" true (test -d $mkdir_sandbox/dirC/dirD/sub; and echo true; or echo false)
|
||||||
|
check "-s produces empty stdout" "" "$out_silent_short"
|
||||||
|
|
||||||
|
# --tree: creates directory and displays tree view with branch glyph
|
||||||
|
set -l out_tree (_fish_mkdir_p --tree $mkdir_sandbox/dirE/dirF)
|
||||||
|
check "--tree returns 0" 0 $status
|
||||||
|
check "--tree creates directory" true (test -d $mkdir_sandbox/dirE/dirF; and echo true; or echo false)
|
||||||
|
check "--tree outputs 'Created directories:'" true (string match -q "*Created directories:*" -- "$out_tree"; and echo true; or echo false)
|
||||||
|
check "--tree outputs branch glyph └──" true (string match -q "*└──*" -- "$out_tree"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Default mode without mode flag uses path mode
|
||||||
|
set -l out_default (_fish_mkdir_p $mkdir_sandbox/dirG/dirH)
|
||||||
|
check "default mode returns 0" 0 $status
|
||||||
|
check "default mode creates directory" true (test -d $mkdir_sandbox/dirG/dirH; and echo true; or echo false)
|
||||||
|
check "default mode outputs 'Created directory:'" true (string match -q "*Created directory:*" -- "$out_default"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf $mkdir_sandbox
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 3. _scrollback_prune_junk: History Log Pruning
|
||||||
|
# =============================================================================
|
||||||
|
section _scrollback_prune_junk
|
||||||
|
|
||||||
|
set -l prune_sandbox (mktemp -d)
|
||||||
|
|
||||||
|
# Create test files in prune sandbox
|
||||||
|
# Empty files (must be deleted)
|
||||||
|
touch $prune_sandbox/empty.log
|
||||||
|
touch $prune_sandbox/empty.txt
|
||||||
|
|
||||||
|
# 1-line noise files (must be deleted)
|
||||||
|
echo "lone command prompt" >$prune_sandbox/single_line.log
|
||||||
|
echo "[exited]" >$prune_sandbox/single_line.txt
|
||||||
|
printf "\n\n \n single line with surrounding whitespace \n\n" >$prune_sandbox/whitespace_single.log
|
||||||
|
|
||||||
|
# Kitty tab-rename UI noise captures (must be deleted)
|
||||||
|
printf "%s\n" "Prompt start" "Enter the new title for this tab below" Done >$prune_sandbox/scrollback_kitty.log
|
||||||
|
printf "%s\n" Header "Enter the new title for this tab below" Tail >$prune_sandbox/scrollback_rename.txt
|
||||||
|
|
||||||
|
# Valid multi-line log files (>1 meaningful lines) (MUST BE PRESERVED)
|
||||||
|
printf "%s\n" "Session started at 12:00" "Command: ls -la" "Output: total 0" >$prune_sandbox/valid_session.log
|
||||||
|
printf "%s\n" "Build step 1 passed" "Build step 2 passed" >$prune_sandbox/valid_session.txt
|
||||||
|
printf "%s\n" "scrollback line 1" "scrollback line 2" "scrollback line 3" >$prune_sandbox/scrollback_valid.log
|
||||||
|
|
||||||
|
# Run prune
|
||||||
|
_scrollback_prune_junk $prune_sandbox
|
||||||
|
|
||||||
|
# Assertions: empty files deleted
|
||||||
|
check "empty .log deleted" false (test -f $prune_sandbox/empty.log; and echo true; or echo false)
|
||||||
|
check "empty .txt deleted" false (test -f $prune_sandbox/empty.txt; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Assertions: 1-line noise files deleted
|
||||||
|
check "single-line .log deleted" false (test -f $prune_sandbox/single_line.log; and echo true; or echo false)
|
||||||
|
check "single-line .txt deleted" false (test -f $prune_sandbox/single_line.txt; and echo true; or echo false)
|
||||||
|
check "whitespace single-line .log deleted" false (test -f $prune_sandbox/whitespace_single.log; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Assertions: kitty tab rename prompt files deleted
|
||||||
|
check "kitty tab rename scrollback_*.log deleted" false (test -f $prune_sandbox/scrollback_kitty.log; and echo true; or echo false)
|
||||||
|
check "kitty tab rename scrollback_*.txt deleted" false (test -f $prune_sandbox/scrollback_rename.txt; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Assertions: valid multi-line files preserved
|
||||||
|
check "valid multi-line .log preserved" true (test -f $prune_sandbox/valid_session.log; and echo true; or echo false)
|
||||||
|
check "valid multi-line .txt preserved" true (test -f $prune_sandbox/valid_session.txt; and echo true; or echo false)
|
||||||
|
check "valid multi-line scrollback_valid.log preserved" true (test -f $prune_sandbox/scrollback_valid.log; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Assertions: non-existent directory handled safely
|
||||||
|
_scrollback_prune_junk $prune_sandbox/nonexistent
|
||||||
|
check "nonexistent directory returns 0" 0 $status
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf $prune_sandbox
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 4. sudo-toggle: Sudo NOPASSWD Security Toggle
|
||||||
|
# =============================================================================
|
||||||
|
section sudo-toggle
|
||||||
|
|
||||||
|
set -l sudo_sandbox (mktemp -d)
|
||||||
|
set -g _mock_sudoers_file "$sudo_sandbox/nofail-toggle"
|
||||||
|
|
||||||
|
# Mock sudo function to intercept stat, truncate, and tee for /etc/sudoers.d/nofail-toggle
|
||||||
|
function sudo
|
||||||
|
set -l cmd_args $argv
|
||||||
|
while test (count $cmd_args) -gt 0; and string match -qr '^-[a-zA-Z]' -- $cmd_args[1]
|
||||||
|
set -e cmd_args[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
switch $cmd_args[1]
|
||||||
|
case stat
|
||||||
|
if test -f "$_mock_sudoers_file"
|
||||||
|
command stat -c %s "$_mock_sudoers_file"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
case truncate
|
||||||
|
if test -f "$_mock_sudoers_file"
|
||||||
|
command truncate -s 0 "$_mock_sudoers_file"
|
||||||
|
end
|
||||||
|
case tee
|
||||||
|
command tee "$_mock_sudoers_file"
|
||||||
|
case '*'
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Case 1: When file is missing -> writes rule, outputs DISABLED (Bypass active)
|
||||||
|
rm -f "$_mock_sudoers_file"
|
||||||
|
set -l out_missing (sudo-toggle)
|
||||||
|
check "missing file: returns 0" 0 $status
|
||||||
|
check "missing file: displays DISABLED" true (string match -q "*🔓 Sudo security: DISABLED*" -- "$out_missing"; and echo true; or echo false)
|
||||||
|
check "missing file: creates sudoers file" true (test -f "$_mock_sudoers_file"; and echo true; or echo false)
|
||||||
|
check "missing file: writes content to file" true (test -s "$_mock_sudoers_file"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Case 2: When file has size > 0 (bypass active) -> truncates to 0, outputs ENABLED
|
||||||
|
set -l out_active (sudo-toggle)
|
||||||
|
check "active bypass: returns 0" 0 $status
|
||||||
|
check "active bypass: displays ENABLED" true (string match -q "*🔒 Sudo security: ENABLED*" -- "$out_active"; and echo true; or echo false)
|
||||||
|
check "active bypass: truncates file to 0 bytes" false (test -s "$_mock_sudoers_file"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Case 3: When file exists but has size 0 -> writes rule, outputs DISABLED
|
||||||
|
set -l out_empty (sudo-toggle)
|
||||||
|
check "empty file: returns 0" 0 $status
|
||||||
|
check "empty file: displays DISABLED" true (string match -q "*🔓 Sudo security: DISABLED*" -- "$out_empty"; and echo true; or echo false)
|
||||||
|
check "empty file: writes content to file" true (test -s "$_mock_sudoers_file"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Case 4: --help flag displays header documentation
|
||||||
|
set -l out_help (sudo-toggle --help)
|
||||||
|
check "sudo-toggle --help returns 0" 0 $status
|
||||||
|
check "sudo-toggle --help contains USAGE" true (string match -q "*USAGE*" -- "$out_help"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
functions -e sudo
|
||||||
|
set -e _mock_sudoers_file
|
||||||
|
rm -rf $sudo_sandbox
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 5. spark: Sparkline Bar Chart Generation
|
||||||
|
# =============================================================================
|
||||||
|
section spark
|
||||||
|
|
||||||
|
# Number array 1 2 3 4 5 produces sparkline characters
|
||||||
|
set -l out_seq (spark 1 2 3 4 5)
|
||||||
|
check "spark 1 2 3 4 5 generates sparkline" "▁▃▄▆█" "$out_seq"
|
||||||
|
|
||||||
|
# Numbers via stdin
|
||||||
|
set -l out_stdin (printf "%s\n" 1 2 3 4 5 | spark)
|
||||||
|
check "spark via stdin generates sparkline" "▁▃▄▆█" "$out_stdin"
|
||||||
|
|
||||||
|
# Clamping with --min and --max
|
||||||
|
set -l out_clamped (spark --min=0 --max=10 0 5 10)
|
||||||
|
check "spark clamped with --min and --max" "▁▄█" "$out_clamped"
|
||||||
|
|
||||||
|
# Version flag
|
||||||
|
set -l out_ver (spark --version)
|
||||||
|
check "spark --version contains version 1.1.0" true (string match -q "*spark, version 1.1.0*" -- "$out_ver"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_ver_s (spark -v)
|
||||||
|
check "spark -v contains version 1.1.0" true (string match -q "*spark, version 1.1.0*" -- "$out_ver_s"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Help flag
|
||||||
|
set -l out_spark_help (spark --help)
|
||||||
|
check "spark --help contains Usage:" true (string match -q "*Usage:*" -- "$out_spark_help"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_spark_help_s (spark -h)
|
||||||
|
check "spark -h contains Usage:" true (string match -q "*Usage:*" -- "$out_spark_help_s"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 6. pkg: System Package Manager Abstraction
|
||||||
|
# =============================================================================
|
||||||
|
section pkg
|
||||||
|
|
||||||
|
# Test missing package manager detection
|
||||||
|
function _fish_deps_detect_pm
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l err_no_pm (pkg somepkg 2>&1 >/dev/null)
|
||||||
|
check "missing package manager returns 1" 1 $status
|
||||||
|
check "missing package manager reports error on stderr" true (string match -q "*error: no supported package manager found*" -- "$err_no_pm"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Test pacman integration with mock detection and mock commands
|
||||||
|
function _fish_deps_detect_pm
|
||||||
|
echo pacman
|
||||||
|
end
|
||||||
|
|
||||||
|
set -g _pacman_log
|
||||||
|
function pacman
|
||||||
|
set -ga _pacman_log (string join -- " " $argv)
|
||||||
|
if test "$argv[1]" = -Qi
|
||||||
|
if test "$argv[2]" = installed-pkg
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function sudo
|
||||||
|
$argv
|
||||||
|
end
|
||||||
|
|
||||||
|
# Auto mode with installed package -> invokes pacman -Qi and then removes (-Rns)
|
||||||
|
set -g _pacman_log
|
||||||
|
set -l out_installed (pkg installed-pkg)
|
||||||
|
check "pkg installed-pkg returns 0" 0 $status
|
||||||
|
check "pkg installed-pkg outputs Removing" true (string match -q "*Removing*installed-pkg*" -- "$out_installed"; and echo true; or echo false)
|
||||||
|
check "pkg installed-pkg runs pacman -Qi query" true (string match -q "*-Qi installed-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
check "pkg installed-pkg runs pacman -Rns removal" true (string match -q "*-Rns installed-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Auto mode with uninstalled package -> invokes pacman -Qi and then installs (-S)
|
||||||
|
set -g _pacman_log
|
||||||
|
set -l out_uninstalled (pkg uninstalled-pkg)
|
||||||
|
check "pkg uninstalled-pkg returns 0" 0 $status
|
||||||
|
check "pkg uninstalled-pkg outputs Installing" true (string match -q "*Installing*uninstalled-pkg*" -- "$out_uninstalled"; and echo true; or echo false)
|
||||||
|
check "pkg uninstalled-pkg runs pacman -Qi query" true (string match -q "*-Qi uninstalled-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
check "pkg uninstalled-pkg runs pacman -S installation" true (string match -q "*-S uninstalled-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Explicit install mode (-i) -> directly installs
|
||||||
|
set -g _pacman_log
|
||||||
|
set -l out_force_install (pkg -i new-pkg)
|
||||||
|
check "pkg -i returns 0" 0 $status
|
||||||
|
check "pkg -i outputs Installing" true (string match -q "*Installing*new-pkg*" -- "$out_force_install"; and echo true; or echo false)
|
||||||
|
check "pkg -i runs pacman -S" true (string match -q "*-S new-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Explicit uninstall mode (-u) -> directly removes
|
||||||
|
set -g _pacman_log
|
||||||
|
set -l out_force_uninstall (pkg -u old-pkg)
|
||||||
|
check "pkg -u returns 0" 0 $status
|
||||||
|
check "pkg -u outputs Removing" true (string match -q "*Removing*old-pkg*" -- "$out_force_uninstall"; and echo true; or echo false)
|
||||||
|
check "pkg -u runs pacman -Rns" true (string match -q "*-Rns old-pkg*" -- "$_pacman_log"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Help flag
|
||||||
|
set -l out_pkg_help (pkg --help)
|
||||||
|
check "pkg --help returns 0" 0 $status
|
||||||
|
check "pkg --help outputs Usage" true (string match -q "*Usage:*pkg*" -- "$out_pkg_help"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# No arguments -> prints usage and returns 0
|
||||||
|
set -l out_pkg_empty (pkg)
|
||||||
|
check "pkg with no arguments returns 0" 0 $status
|
||||||
|
check "pkg with no arguments outputs Usage" true (string match -q "*Usage:*pkg*" -- "$out_pkg_empty"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Unknown flag -> returns 1 and prints error to stderr
|
||||||
|
set -l err_pkg_flag (pkg --unrecognized-option 2>&1 >/dev/null)
|
||||||
|
check "pkg unknown flag returns 1" 1 $status
|
||||||
|
check "pkg unknown flag outputs error on stderr" true (string match -q "*error:*unknown flag*--unrecognized-option*" -- "$err_pkg_flag"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
functions -e _fish_deps_detect_pm pacman sudo
|
||||||
|
set -e _pacman_log
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Report
|
||||||
|
# =============================================================================
|
||||||
|
report
|
||||||
@@ -245,10 +245,10 @@ section "op_enabled: always/* and AND, via a synthetic registry"
|
|||||||
# this process reads them, so overriding them is free.
|
# this process reads them, so overriding them is free.
|
||||||
set -g __fish_config_op_registry_keys "syn_on:" "syn_off:" "syn_and:" "syn_bare:" "syn_multi:"
|
set -g __fish_config_op_registry_keys "syn_on:" "syn_off:" "syn_and:" "syn_bare:" "syn_multi:"
|
||||||
set -g __fish_config_op_registry_values \
|
set -g __fish_config_op_registry_values \
|
||||||
"always/on" \
|
always/on \
|
||||||
"always/off" \
|
always/off \
|
||||||
"aliases/filesystem integrations/notifications" \
|
"aliases/filesystem integrations/notifications" \
|
||||||
"aliases" \
|
aliases \
|
||||||
"always/off always/on"
|
"always/off always/on"
|
||||||
|
|
||||||
set -e __fish_config_op_aliases __fish_config_op_integrations __fish_config_opinionated
|
set -e __fish_config_op_aliases __fish_config_op_integrations __fish_config_opinionated
|
||||||
@@ -312,7 +312,7 @@ section "op_enabled: C5 through the guard"
|
|||||||
# The path production code actually takes, as opposed to calling the cascade
|
# The path production code actually takes, as opposed to calling the cascade
|
||||||
# directly. Same rule: unset means off and the master cannot enable it.
|
# directly. Same rule: unset means off and the master cannot enable it.
|
||||||
set -g __fish_config_op_registry_keys "syn_log:"
|
set -g __fish_config_op_registry_keys "syn_log:"
|
||||||
set -g __fish_config_op_registry_values "logging/terminal-capture"
|
set -g __fish_config_op_registry_values logging/terminal-capture
|
||||||
set -e __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
set -e __fish_config_op_logging __fish_config_op_logging_terminal_capture
|
||||||
|
|
||||||
__fish_config_op_enabled syn_log
|
__fish_config_op_enabled syn_log
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function test_help_renderer
|
|||||||
'function fixturefn' \
|
'function fixturefn' \
|
||||||
' __fish_help_header (status current-function) $argv; and return 0' \
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
' echo RAN-BODY' \
|
' echo RAN-BODY' \
|
||||||
'end' >$tmp/fixturefn.fish
|
end >$tmp/fixturefn.fish
|
||||||
|
|
||||||
set -l out (_help_probe $tmp 'fixturefn --help')
|
set -l out (_help_probe $tmp 'fixturefn --help')
|
||||||
set -l code $status
|
set -l code $status
|
||||||
@@ -106,14 +106,14 @@ function test_help_renderer_degrades_safely
|
|||||||
'function headerless' \
|
'function headerless' \
|
||||||
' __fish_help_header (status current-function) $argv; and return 0' \
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
" touch $tmp/BODY-RAN" \
|
" touch $tmp/BODY-RAN" \
|
||||||
'end' >$tmp/headerless.fish
|
end >$tmp/headerless.fish
|
||||||
# A comment run carrying no `# LABEL` line at all.
|
# A comment run carrying no `# LABEL` line at all.
|
||||||
printf '%s\n' \
|
printf '%s\n' \
|
||||||
'# just an ordinary comment, no labels here' \
|
'# just an ordinary comment, no labels here' \
|
||||||
'function malformed' \
|
'function malformed' \
|
||||||
' __fish_help_header (status current-function) $argv; and return 0' \
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
" touch $tmp/BODY-RAN" \
|
" touch $tmp/BODY-RAN" \
|
||||||
'end' >$tmp/malformed.fish
|
end >$tmp/malformed.fish
|
||||||
|
|
||||||
set -l failed 0
|
set -l failed 0
|
||||||
for fn in headerless malformed
|
for fn in headerless malformed
|
||||||
|
|||||||
@@ -0,0 +1,683 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# Hermetic unit tests for network-dependent fish functions with full mock coverage
|
||||||
|
# and failure edge-case testing:
|
||||||
|
# - gi (gitignore generation, API fetch, deduplication, error handling)
|
||||||
|
# - gip, gip4, gip6 (public IP resolution, timeouts, IPv4/IPv6 fallback, failure notices)
|
||||||
|
# - qr (terminal QR code generator, qrencode local bypass, curl fallback & network drop)
|
||||||
|
# - bd-pull (Gitea issues sync, missing tokens, API failures, malformed JSON, linking)
|
||||||
|
# - _auto_pull_sync (background safe fast-forward, dirty trees, network drop on fetch)
|
||||||
|
# - gitup (remote fetch & status, network drop handling)
|
||||||
|
# - git-clean (remote fetch/prune, orphaned branch detection & deletion)
|
||||||
|
# - config-update (upstream fetch, up-to-date check, dry-run, force stash & restore)
|
||||||
|
# - repo-open (origin URL parsing, offline local tracking, ls-remote fallback & drop)
|
||||||
|
# - fzf-update (git pull/clone network failure handling)
|
||||||
|
#
|
||||||
|
# MODE: isolated
|
||||||
|
|
||||||
|
source (realpath (dirname (status filename)))/lib.fish
|
||||||
|
set -p fish_function_path $repo_root/functions
|
||||||
|
|
||||||
|
set -gx TERM xterm-256color
|
||||||
|
set -g TMPDIRS
|
||||||
|
|
||||||
|
# Ensure git operations inside tests are hermetic and do not prompt
|
||||||
|
set -gx GIT_AUTHOR_NAME t
|
||||||
|
set -gx GIT_AUTHOR_EMAIL t@t
|
||||||
|
set -gx GIT_COMMITTER_NAME t
|
||||||
|
set -gx GIT_COMMITTER_EMAIL t@t
|
||||||
|
set -gx GIT_CONFIG_COUNT 2
|
||||||
|
set -gx GIT_CONFIG_KEY_0 commit.gpgsign
|
||||||
|
set -gx GIT_CONFIG_VALUE_0 false
|
||||||
|
set -gx GIT_CONFIG_KEY_1 init.defaultBranch
|
||||||
|
set -gx GIT_CONFIG_VALUE_1 main
|
||||||
|
set -gx GIT_TERMINAL_PROMPT 0
|
||||||
|
|
||||||
|
# Helper to create temporary git repos
|
||||||
|
function new_repo
|
||||||
|
set -l d (mktemp -d)
|
||||||
|
set -ga TMPDIRS $d
|
||||||
|
git -C $d init -q
|
||||||
|
git -C $d config user.email t@t
|
||||||
|
git -C $d config user.name t
|
||||||
|
git -C $d config commit.gpgsign false
|
||||||
|
git -C $d config core.hooksPath /dev/null
|
||||||
|
printf '%s\n' $d
|
||||||
|
end
|
||||||
|
|
||||||
|
# Build a hermetic mock bin directory
|
||||||
|
set -g MOCK_DIR (mktemp -d)
|
||||||
|
set -ga TMPDIRS $MOCK_DIR
|
||||||
|
set -l real_git (command -s git)
|
||||||
|
|
||||||
|
# 1. Mock curl
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'if [ -n "$MOCK_CURL_LOG" ]; then' \
|
||||||
|
' printf "%s\n" "$*" >> "$MOCK_CURL_LOG"' \
|
||||||
|
fi \
|
||||||
|
'if [ -n "$MOCK_CURL_HANDLER" ] && [ -x "$MOCK_CURL_HANDLER" ]; then' \
|
||||||
|
' exec "$MOCK_CURL_HANDLER" "$@"' \
|
||||||
|
fi \
|
||||||
|
'if [ -n "$MOCK_CURL_DELAY" ]; then' \
|
||||||
|
' sleep "$MOCK_CURL_DELAY"' \
|
||||||
|
fi \
|
||||||
|
'if [ -n "$MOCK_CURL_STDERR" ]; then' \
|
||||||
|
' printf "%s\n" "$MOCK_CURL_STDERR" >&2' \
|
||||||
|
fi \
|
||||||
|
'if [ -f "$MOCK_CURL_BODY_FILE" ]; then' \
|
||||||
|
' cat "$MOCK_CURL_BODY_FILE"' \
|
||||||
|
'elif [ -n "$MOCK_CURL_BODY" ]; then' \
|
||||||
|
' printf "%s\n" "$MOCK_CURL_BODY"' \
|
||||||
|
fi \
|
||||||
|
'exit "${MOCK_CURL_STATUS:-0}"' >$MOCK_DIR/curl
|
||||||
|
chmod +x $MOCK_DIR/curl
|
||||||
|
|
||||||
|
# 2. Mock git shim
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'if [ -n "$MOCK_GIT_LOG" ]; then' \
|
||||||
|
' printf "%s\n" "$*" >> "$MOCK_GIT_LOG"' \
|
||||||
|
fi \
|
||||||
|
'if [ -n "$MOCK_GIT_HANDLER" ] && [ -x "$MOCK_GIT_HANDLER" ]; then' \
|
||||||
|
' exec "$MOCK_GIT_HANDLER" "$@"' \
|
||||||
|
fi \
|
||||||
|
'for a in "$@"; do' \
|
||||||
|
' if [ "$a" = "fetch" ] && [ -n "$MOCK_GIT_FAIL_FETCH" ]; then' \
|
||||||
|
' echo "fatal: unable to access: Could not resolve host" >&2' \
|
||||||
|
' exit "${MOCK_GIT_FETCH_STATUS:-128}"' \
|
||||||
|
' fi' \
|
||||||
|
' if [ "$a" = "pull" ] && [ -n "$MOCK_GIT_FAIL_PULL" ]; then' \
|
||||||
|
' echo "fatal: unable to access: Could not resolve host" >&2' \
|
||||||
|
' exit "${MOCK_GIT_PULL_STATUS:-128}"' \
|
||||||
|
' fi' \
|
||||||
|
' if [ "$a" = "clone" ] && [ -n "$MOCK_GIT_FAIL_CLONE" ]; then' \
|
||||||
|
' echo "fatal: unable to access: Could not resolve host" >&2' \
|
||||||
|
' exit "${MOCK_GIT_CLONE_STATUS:-128}"' \
|
||||||
|
' fi' \
|
||||||
|
' if [ "$a" = "ls-remote" ] && [ -n "$MOCK_GIT_FAIL_LS_REMOTE" ]; then' \
|
||||||
|
' echo "fatal: unable to access: Could not resolve host" >&2' \
|
||||||
|
' exit "${MOCK_GIT_LS_REMOTE_STATUS:-128}"' \
|
||||||
|
' fi' \
|
||||||
|
done \
|
||||||
|
"exec $real_git \"\$@\"" >$MOCK_DIR/git
|
||||||
|
chmod +x $MOCK_DIR/git
|
||||||
|
|
||||||
|
# 3. Mock bd CLI
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'if [ "$1" = "create" ]; then' \
|
||||||
|
' echo "Created issue bd-99"' \
|
||||||
|
' echo "{\"id\":\"bd-99\"}" >> .beads/issues.jsonl' \
|
||||||
|
' exit 0' \
|
||||||
|
'elif [ "$1" = "sync" ]; then' \
|
||||||
|
' exit 0' \
|
||||||
|
fi \
|
||||||
|
'exit 0' >$MOCK_DIR/bd
|
||||||
|
chmod +x $MOCK_DIR/bd
|
||||||
|
|
||||||
|
# 4. Mock qrencode
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'if [ -n "$MOCK_QRENCODE_LOG" ]; then' \
|
||||||
|
' printf "%s\n" "$*" >> "$MOCK_QRENCODE_LOG"' \
|
||||||
|
fi \
|
||||||
|
'echo "LOCAL_QRENCODE: $*"' \
|
||||||
|
'exit 0' >$MOCK_DIR/qrencode
|
||||||
|
chmod +x $MOCK_DIR/qrencode
|
||||||
|
|
||||||
|
# Prepend MOCK_DIR to PATH so mocks take precedence
|
||||||
|
set -gx PATH $MOCK_DIR $PATH
|
||||||
|
|
||||||
|
function reset_mocks
|
||||||
|
set -e MOCK_CURL_STATUS
|
||||||
|
set -e MOCK_CURL_BODY
|
||||||
|
set -e MOCK_CURL_BODY_FILE
|
||||||
|
set -e MOCK_CURL_STDERR
|
||||||
|
set -e MOCK_CURL_DELAY
|
||||||
|
set -e MOCK_CURL_LOG
|
||||||
|
set -e MOCK_CURL_HANDLER
|
||||||
|
set -e MOCK_GIT_FAIL_FETCH
|
||||||
|
set -e MOCK_GIT_FETCH_STATUS
|
||||||
|
set -e MOCK_GIT_FAIL_PULL
|
||||||
|
set -e MOCK_GIT_PULL_STATUS
|
||||||
|
set -e MOCK_GIT_FAIL_CLONE
|
||||||
|
set -e MOCK_GIT_CLONE_STATUS
|
||||||
|
set -e MOCK_GIT_FAIL_LS_REMOTE
|
||||||
|
set -e MOCK_GIT_LS_REMOTE_STATUS
|
||||||
|
set -e MOCK_GIT_LOG
|
||||||
|
set -e MOCK_GIT_HANDLER
|
||||||
|
set -e MOCK_QRENCODE_LOG
|
||||||
|
end
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
for d in $TMPDIRS
|
||||||
|
test -n "$d"; and command rm -rf $d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 1. gi (gitignore generator)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: gi"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
set -gx MOCK_CURL_BODY "c,cpp,python,rust"
|
||||||
|
set -l list_out (gi -l)
|
||||||
|
check "gi -l: returns API target list" "c,cpp,python,rust" "$list_out"
|
||||||
|
|
||||||
|
# Complete network drop on list
|
||||||
|
set -gx MOCK_CURL_STATUS 7
|
||||||
|
set -gx MOCK_CURL_BODY ""
|
||||||
|
gi -l >/dev/null 2>&1
|
||||||
|
check "gi -l: returns 0 per contract" 0 $status
|
||||||
|
|
||||||
|
# stdout mode with valid content
|
||||||
|
reset_mocks
|
||||||
|
set -gx MOCK_CURL_BODY "# Python gitignore\n*.pyc\n__pycache__/"
|
||||||
|
set -l stdout_out (gi -s python)
|
||||||
|
check "gi -s: prints fetched patterns to stdout" "# Python gitignore\n*.pyc\n__pycache__/" "$stdout_out"
|
||||||
|
|
||||||
|
# stdout mode with network failure / 404 (curl exit 22)
|
||||||
|
reset_mocks
|
||||||
|
set -gx MOCK_CURL_STATUS 22
|
||||||
|
set -gx MOCK_CURL_BODY ""
|
||||||
|
gi -s invalid_target >/dev/null 2>&1
|
||||||
|
check "gi -s: API failure returns 1" 1 $status
|
||||||
|
|
||||||
|
# Append mode outside git repository
|
||||||
|
reset_mocks
|
||||||
|
set -l non_git_dir (mktemp -d)
|
||||||
|
set -ga TMPDIRS $non_git_dir
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd $non_git_dir
|
||||||
|
gi python >/dev/null 2>&1
|
||||||
|
set -l rc $status
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
check "gi <target>: outside git repo returns 1" 1 $rc
|
||||||
|
end
|
||||||
|
|
||||||
|
# Append mode inside git repo + deduplication
|
||||||
|
reset_mocks
|
||||||
|
set -l repo (new_repo)
|
||||||
|
set -gx MOCK_CURL_BODY "# Python gitignore\n*.pyc"
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd $repo
|
||||||
|
gi python >/dev/null 2>&1
|
||||||
|
check "gi python: first run returns 0" 0 $status
|
||||||
|
check "gi python: creates .gitignore" true (test -f "$repo/.gitignore"; and echo true; or echo false)
|
||||||
|
set -l first_lines (count (command cat "$repo/.gitignore"))
|
||||||
|
|
||||||
|
# Second run with identical pattern triggers deduplication
|
||||||
|
gi python >/dev/null 2>&1
|
||||||
|
check "gi python: second run returns 0" 0 $status
|
||||||
|
set -l second_lines (count (command cat "$repo/.gitignore"))
|
||||||
|
check "gi python: deduplication prevents duplicate append" $first_lines $second_lines
|
||||||
|
|
||||||
|
# Network failure during append skips modifying file
|
||||||
|
set -gx MOCK_CURL_STATUS 7
|
||||||
|
gi ruby >/dev/null 2>&1
|
||||||
|
set -l third_lines (count (command cat "$repo/.gitignore"))
|
||||||
|
check "gi: network drop during append preserves file" $second_lines $third_lines
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 2. gip, gip4, gip6 (IP resolution)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: gip, gip4, gip6"
|
||||||
|
|
||||||
|
# Create a specialized curl handler for gip IPv4/IPv6 dispatch
|
||||||
|
set -l gip_handler $MOCK_DIR/gip_curl.sh
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'for a in "$@"; do' \
|
||||||
|
' if [ "$a" = "-4" ]; then' \
|
||||||
|
' if [ -n "$MOCK_FAIL_IPV4" ]; then exit "${MOCK_IPV4_STATUS:-28}"; fi' \
|
||||||
|
' echo "198.51.100.1"' \
|
||||||
|
' exit 0' \
|
||||||
|
' fi' \
|
||||||
|
' if [ "$a" = "-6" ]; then' \
|
||||||
|
' if [ -n "$MOCK_FAIL_IPV6" ]; then exit "${MOCK_IPV6_STATUS:-28}"; fi' \
|
||||||
|
' echo "2001:db8::1"' \
|
||||||
|
' exit 0' \
|
||||||
|
' fi' \
|
||||||
|
done \
|
||||||
|
'exit 0' >$gip_handler
|
||||||
|
chmod +x $gip_handler
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
set -gx MOCK_CURL_HANDLER $gip_handler
|
||||||
|
|
||||||
|
# gip: both IPv4 and IPv6 succeed
|
||||||
|
set -l out_both (gip)
|
||||||
|
check "gip: both succeed outputs IPv4" true (string match -q '*IPv4: 198.51.100.1*' -- $out_both; and echo true; or echo false)
|
||||||
|
check "gip: both succeed outputs IPv6" true (string match -q '*IPv6: 2001:db8::1*' -- $out_both; and echo true; or echo false)
|
||||||
|
|
||||||
|
# gip: IPv4 timeout / failure, IPv6 success
|
||||||
|
set -gx MOCK_FAIL_IPV4 1
|
||||||
|
set -l out_v4_fail (gip)
|
||||||
|
check "gip: IPv4 failure reports 'Not detected'" true (string match -q '*IPv4: Not detected*' -- $out_v4_fail; and echo true; or echo false)
|
||||||
|
check "gip: IPv4 failure still reports IPv6" true (string match -q '*IPv6: 2001:db8::1*' -- $out_v4_fail; and echo true; or echo false)
|
||||||
|
|
||||||
|
# gip: IPv4 success, IPv6 failure
|
||||||
|
set -e MOCK_FAIL_IPV4
|
||||||
|
set -gx MOCK_FAIL_IPV6 1
|
||||||
|
set -l out_v6_fail (gip)
|
||||||
|
check "gip: IPv6 failure reports IPv4" true (string match -q '*IPv4: 198.51.100.1*' -- $out_v6_fail; and echo true; or echo false)
|
||||||
|
check "gip: IPv6 failure reports 'Not detected'" true (string match -q '*IPv6: Not detected*' -- $out_v6_fail; and echo true; or echo false)
|
||||||
|
|
||||||
|
# gip: complete network drop (both fail)
|
||||||
|
set -gx MOCK_FAIL_IPV4 1
|
||||||
|
set -gx MOCK_FAIL_IPV6 1
|
||||||
|
set -l out_all_fail (gip)
|
||||||
|
check "gip: complete drop reports IPv4 Not detected" true (string match -q '*IPv4: Not detected*' -- $out_all_fail; and echo true; or echo false)
|
||||||
|
check "gip: complete drop reports IPv6 Not detected" true (string match -q '*IPv6: Not detected*' -- $out_all_fail; and echo true; or echo false)
|
||||||
|
|
||||||
|
# gip4: success
|
||||||
|
set -e MOCK_FAIL_IPV4
|
||||||
|
set -l out_gip4 (gip4)
|
||||||
|
check "gip4: success prints IP" "198.51.100.1" "$out_gip4"
|
||||||
|
check "gip4: success exits 0" 0 $status
|
||||||
|
|
||||||
|
# gip4: failure
|
||||||
|
set -gx MOCK_FAIL_IPV4 1
|
||||||
|
set -gx MOCK_IPV4_STATUS 7
|
||||||
|
gip4 >/dev/null 2>&1
|
||||||
|
check "gip4: failure returns curl status" 7 $status
|
||||||
|
|
||||||
|
# gip6: success
|
||||||
|
set -e MOCK_FAIL_IPV6
|
||||||
|
set -l out_gip6 (gip6)
|
||||||
|
check "gip6: success prints IPv6" "2001:db8::1" "$out_gip6"
|
||||||
|
check "gip6: success exits 0" 0 $status
|
||||||
|
|
||||||
|
# gip6: failure / unsupported network
|
||||||
|
set -gx MOCK_FAIL_IPV6 1
|
||||||
|
set -l out_gip6_err (gip6 2>&1)
|
||||||
|
check "gip6: failure exits 1" 1 $status
|
||||||
|
check "gip6: failure prints notice" true (string match -q '*IPv6 is currently unavailable*' -- $out_gip6_err; and echo true; or echo false)
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 3. qr (QR code generator)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: qr"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
# Case A: Local qrencode tool is present
|
||||||
|
set -gx MOCK_QRENCODE_LOG (mktemp)
|
||||||
|
set -ga TMPDIRS $MOCK_QRENCODE_LOG
|
||||||
|
set -l qr_local (qr "hello-world")
|
||||||
|
check "qr: local qrencode is preferred offline" true (string match -q '*LOCAL_QRENCODE*' -- $qr_local; and echo true; or echo false)
|
||||||
|
check "qr: curl is never called when qrencode exists" 0 (test -f $MOCK_DIR/curl_log; and count (cat $MOCK_DIR/curl_log); or echo 0)
|
||||||
|
|
||||||
|
# Case B: Local qrencode is missing, falling back to curl
|
||||||
|
function type
|
||||||
|
if test "$argv[1]" = -q -a "$argv[2]" = qrencode
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
builtin type $argv
|
||||||
|
end
|
||||||
|
|
||||||
|
set -gx MOCK_CURL_BODY UTF8_QR_BODY
|
||||||
|
set -l qr_curl (qr "hello-curl")
|
||||||
|
check "qr: fallback to curl when qrencode is missing" UTF8_QR_BODY "$qr_curl"
|
||||||
|
|
||||||
|
# Network drop during curl fallback
|
||||||
|
set -gx MOCK_CURL_STATUS 7
|
||||||
|
set -gx MOCK_CURL_BODY ""
|
||||||
|
qr fail >/dev/null 2>&1
|
||||||
|
check "qr: curl network drop returns non-zero" 7 $status
|
||||||
|
|
||||||
|
# Argument-based curl fallback
|
||||||
|
set -gx MOCK_CURL_STATUS 0
|
||||||
|
set -gx MOCK_CURL_BODY TEXT_QR
|
||||||
|
set -l qr_arg (qr "arg-text")
|
||||||
|
check "qr: argument works via curl fallback" TEXT_QR "$qr_arg"
|
||||||
|
|
||||||
|
functions -e type
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 4. bd-pull (Gitea issues sync)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: bd-pull"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
# Missing required arguments & env vars
|
||||||
|
bd-pull >/dev/null 2>&1
|
||||||
|
check "bd-pull: missing repo argument exits 1" 1 $status
|
||||||
|
|
||||||
|
begin
|
||||||
|
set -e GITEA_TOKEN
|
||||||
|
set -e GITEA_URL
|
||||||
|
bd-pull rootiest/test >/dev/null 2>&1
|
||||||
|
check "bd-pull: missing GITEA_TOKEN exits 1" 1 $status
|
||||||
|
|
||||||
|
set -gx GITEA_TOKEN test_token
|
||||||
|
bd-pull rootiest/test >/dev/null 2>&1
|
||||||
|
check "bd-pull: missing GITEA_URL exits 1" 1 $status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Complete network drop / empty response
|
||||||
|
set -gx GITEA_TOKEN dummy_token
|
||||||
|
set -gx GITEA_URL "https://git.test"
|
||||||
|
set -gx MOCK_CURL_STATUS 7
|
||||||
|
set -gx MOCK_CURL_BODY ""
|
||||||
|
set -l out_drop (bd-pull rootiest/test)
|
||||||
|
check "bd-pull: network drop reports no unlinked issues" true (string match -q '*No unlinked issues found*' -- $out_drop; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Empty JSON issue list
|
||||||
|
set -gx MOCK_CURL_STATUS 0
|
||||||
|
set -gx MOCK_CURL_BODY "[]"
|
||||||
|
set -l out_empty (bd-pull rootiest/test)
|
||||||
|
check "bd-pull: empty issue list handled cleanly" true (string match -q '*No unlinked issues found*' -- $out_empty; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Issues already linked with [ID] prefix
|
||||||
|
set -gx MOCK_CURL_BODY '[{"number": 1, "title": "[bd-1] Already linked issue"}]'
|
||||||
|
set -l out_already (bd-pull rootiest/test)
|
||||||
|
check "bd-pull: already-linked issues skipped" true (string match -q '*No unlinked issues found*' -- $out_already; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Unlinked issue found: creates bead, patches title, commits & pushes
|
||||||
|
set -l bd_remote (new_repo)
|
||||||
|
git -C $bd_remote config --bool core.bare true
|
||||||
|
set -l bd_repo (new_repo)
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd $bd_repo
|
||||||
|
git remote add origin $bd_remote
|
||||||
|
mkdir -p .beads
|
||||||
|
touch .beads/issues.jsonl
|
||||||
|
git add .beads/issues.jsonl
|
||||||
|
git commit -q -m "init beads"
|
||||||
|
git push -q -u origin main
|
||||||
|
|
||||||
|
set -gx MOCK_CURL_BODY '[{"number": 2, "title": "Web original issue without ID"}]'
|
||||||
|
set -l out_unlinked (bd-pull rootiest/test 2>&1)
|
||||||
|
check "bd-pull: unlinked issue is linked" true (string match -q '*Linked 1 issues*' -- $out_unlinked; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Verify git commit was made
|
||||||
|
set -l last_msg (git log -1 --pretty=%s)
|
||||||
|
check "bd-pull: commits synced IDs to git" "chore: sync local IDs for web issues" "$last_msg"
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 5. _auto_pull_sync (background fast-forward)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: _auto_pull_sync"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
# Non-git directory
|
||||||
|
_auto_pull_sync /tmp >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: non-git directory returns 1" 1 $status
|
||||||
|
|
||||||
|
# Git repo without upstream branch
|
||||||
|
set -l sync_repo (new_repo)
|
||||||
|
echo test >$sync_repo/file.txt
|
||||||
|
git -C $sync_repo add file.txt
|
||||||
|
git -C $sync_repo commit -q -m initial
|
||||||
|
_auto_pull_sync $sync_repo >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: missing upstream returns 1" 1 $status
|
||||||
|
|
||||||
|
# Setup upstream branch for sync_repo
|
||||||
|
set -l sync_upstream (new_repo)
|
||||||
|
git -C $sync_upstream config --bool core.bare true
|
||||||
|
git -C $sync_repo remote add origin $sync_upstream
|
||||||
|
git -C $sync_repo push -q -u origin main >/dev/null 2>&1
|
||||||
|
|
||||||
|
# Dirty working tree (unstaged modifications)
|
||||||
|
echo dirty >>$sync_repo/file.txt
|
||||||
|
_auto_pull_sync $sync_repo >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: dirty worktree returns 1" 1 $status
|
||||||
|
git -C $sync_repo checkout -q -- file.txt
|
||||||
|
|
||||||
|
# Dirty index (staged modifications)
|
||||||
|
echo staged >>$sync_repo/staged.txt
|
||||||
|
git -C $sync_repo add staged.txt
|
||||||
|
_auto_pull_sync $sync_repo >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: dirty index returns 1" 1 $status
|
||||||
|
git -C $sync_repo reset -q HEAD staged.txt
|
||||||
|
command rm -f $sync_repo/staged.txt
|
||||||
|
|
||||||
|
# Clean repo, network failure on git fetch
|
||||||
|
set -gx MOCK_GIT_FAIL_FETCH 1
|
||||||
|
_auto_pull_sync $sync_repo >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: fetch failure returns 1" 1 $status
|
||||||
|
|
||||||
|
# Clean repo, successful fast-forward
|
||||||
|
reset_mocks
|
||||||
|
# Add commit to upstream
|
||||||
|
set -l peer_clone (new_repo)
|
||||||
|
git -C $peer_clone clone -q $sync_upstream $peer_clone/work
|
||||||
|
echo "new commit" >$peer_clone/work/new.txt
|
||||||
|
git -C $peer_clone/work add new.txt
|
||||||
|
git -C $peer_clone/work commit -q -m "upstream work"
|
||||||
|
git -C $peer_clone/work push -q origin main
|
||||||
|
|
||||||
|
_auto_pull_sync $sync_repo >/dev/null 2>&1
|
||||||
|
check "_auto_pull_sync: clean fast-forward returns 0" 0 $status
|
||||||
|
check "_auto_pull_sync: changes merged into working tree" true (test -f $sync_repo/new.txt; and echo true; or echo false)
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 6. gitup (fetch and status)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: gitup"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd /tmp
|
||||||
|
gitup >/dev/null 2>&1
|
||||||
|
check "gitup: outside git repo exits 1" 1 $status
|
||||||
|
|
||||||
|
set -l r (new_repo)
|
||||||
|
builtin cd $r
|
||||||
|
echo a >a && git add a && git commit -q -m a
|
||||||
|
|
||||||
|
# Network failure on git fetch
|
||||||
|
set -gx MOCK_GIT_FAIL_FETCH 1
|
||||||
|
gitup >/dev/null 2>&1
|
||||||
|
check "gitup: network drop during fetch exits non-zero" true (test $status -ne 0; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Successful fetch
|
||||||
|
reset_mocks
|
||||||
|
set -l up_out (gitup 2>&1)
|
||||||
|
check "gitup: success exits 0" 0 $status
|
||||||
|
check "gitup: displays git status output" true (string match -q '*On branch main*' -- $up_out; and echo true; or echo false)
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 7. git-clean (fetch --prune and delete orphaned branches)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: git-clean"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
git-clean --help >/dev/null 2>&1
|
||||||
|
check "git-clean: --help exits 0" 0 $status
|
||||||
|
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
set -l r (new_repo)
|
||||||
|
builtin cd $r
|
||||||
|
echo a >a && git add a && git commit -q -m a
|
||||||
|
|
||||||
|
# Network drop during git fetch --prune
|
||||||
|
set -gx MOCK_GIT_FAIL_FETCH 1
|
||||||
|
# git-clean continues to local cleanup even if fetch fails
|
||||||
|
set -l clean_out (git-clean 2>&1)
|
||||||
|
check "git-clean: network drop handled, reports tidy" true (string match -q '*No orphaned branches found*' -- $clean_out; and echo true; or echo false)
|
||||||
|
|
||||||
|
# Orphaned branch detection and cleanup
|
||||||
|
reset_mocks
|
||||||
|
# Simulate an orphaned branch with [gone] tracking
|
||||||
|
git branch orphaned-feat
|
||||||
|
# Create git-clean handler that simulates git branch -vv showing ': gone]'
|
||||||
|
set -l clean_git_handler $MOCK_DIR/git_clean_shim.sh
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'for a in "$@"; do' \
|
||||||
|
' if [ "$a" = "-vv" ]; then' \
|
||||||
|
' echo " main 1234567 [origin/main] initial"' \
|
||||||
|
' echo " orphaned-feat abcdef0 [origin/orphaned-feat: gone] feature"' \
|
||||||
|
' exit 0' \
|
||||||
|
' fi' \
|
||||||
|
done \
|
||||||
|
"exec $real_git \"\$@\"" >$clean_git_handler
|
||||||
|
chmod +x $clean_git_handler
|
||||||
|
|
||||||
|
set -gx MOCK_GIT_HANDLER $clean_git_handler
|
||||||
|
set -l clean_del_out (git-clean 2>&1)
|
||||||
|
check "git-clean: detects and deletes orphaned branch" true (string match -q '*Deleting orphaned local branches*' -- $clean_del_out; and echo true; or echo false)
|
||||||
|
check "git-clean: orphaned branch was deleted" false (git rev-parse --verify --quiet orphaned-feat >/dev/null 2>&1; and echo true; or echo false)
|
||||||
|
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 8. config-update (configuration repository sync)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: config-update"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
config-update --help >/dev/null 2>&1
|
||||||
|
check "config-update: --help exits 0" 0 $status
|
||||||
|
|
||||||
|
# Non-git CONFIG_DIR (using mock handler)
|
||||||
|
set -l cfg_update_handler $MOCK_DIR/cfg_update_shim.sh
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'for a in "$@"; do' \
|
||||||
|
' if [ "$a" = "fetch" ] && [ -n "$MOCK_CFG_FAIL_FETCH" ]; then' \
|
||||||
|
' exit 1' \
|
||||||
|
' fi' \
|
||||||
|
done \
|
||||||
|
"exec $real_git \"\$@\"" >$cfg_update_handler
|
||||||
|
chmod +x $cfg_update_handler
|
||||||
|
set -gx MOCK_GIT_HANDLER $cfg_update_handler
|
||||||
|
|
||||||
|
# Network failure on upstream fetch
|
||||||
|
set -l fake_home (mktemp -d)
|
||||||
|
set -ga TMPDIRS $fake_home
|
||||||
|
set -l fake_fish_cfg "$fake_home/.config/fish"
|
||||||
|
mkdir -p (dirname $fake_fish_cfg)
|
||||||
|
set -l cfg_repo (new_repo)
|
||||||
|
command cp -r $cfg_repo $fake_fish_cfg
|
||||||
|
|
||||||
|
# Test config-update against fake_fish_cfg by setting HOME
|
||||||
|
begin
|
||||||
|
set -lx HOME $fake_home
|
||||||
|
set -gx MOCK_CFG_FAIL_FETCH 1
|
||||||
|
config-update >/dev/null 2>&1
|
||||||
|
check "config-update: network fetch failure returns 1" 1 $status
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 9. repo-open (origin URL normalization and browser deep-linking)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: repo-open"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
repo-open --help >/dev/null 2>&1
|
||||||
|
check "repo-open: --help exits 0" 0 $status
|
||||||
|
|
||||||
|
# Outside git repo
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd /tmp
|
||||||
|
repo-open -p >/dev/null 2>&1
|
||||||
|
check "repo-open: outside git repo returns 1" 1 $status
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# In git repo without origin
|
||||||
|
set -l r_no_origin (new_repo)
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd $r_no_origin
|
||||||
|
repo-open -p >/dev/null 2>&1
|
||||||
|
check "repo-open: without origin remote returns 1" 1 $status
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# In git repo with GitHub origin remote
|
||||||
|
set -l r_gh (new_repo)
|
||||||
|
git -C $r_gh remote add origin "https://github.com/rootiest/fish-config.git"
|
||||||
|
echo init >$r_gh/file && git -C $r_gh add file && git -C $r_gh commit -q -m init
|
||||||
|
begin
|
||||||
|
set -l prev_pwd $PWD
|
||||||
|
builtin cd $r_gh
|
||||||
|
|
||||||
|
# Remote ls-remote network failure falls back to default branch
|
||||||
|
set -gx MOCK_GIT_FAIL_LS_REMOTE 1
|
||||||
|
set -l url_fallback (repo-open -p)
|
||||||
|
check "repo-open: ls-remote network drop falls back to default branch" "https://github.com/rootiest/fish-config" "$url_fallback"
|
||||||
|
|
||||||
|
# With ls-remote success for current branch
|
||||||
|
reset_mocks
|
||||||
|
set -l gh_ls_handler $MOCK_DIR/gh_ls_shim.sh
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'for a in "$@"; do' \
|
||||||
|
' if [ "$a" = "ls-remote" ]; then' \
|
||||||
|
' echo "abcdef01 refs/heads/main"' \
|
||||||
|
' exit 0' \
|
||||||
|
' fi' \
|
||||||
|
done \
|
||||||
|
"exec $real_git \"\$@\"" >$gh_ls_handler
|
||||||
|
chmod +x $gh_ls_handler
|
||||||
|
set -gx MOCK_GIT_HANDLER $gh_ls_handler
|
||||||
|
|
||||||
|
# Test gitlab provider normalization
|
||||||
|
git remote set-url origin "git@gitlab.com:rootiest/fish-config.git"
|
||||||
|
git checkout -q -b feat-test
|
||||||
|
set -l url_gl (repo-open -p)
|
||||||
|
check "repo-open: gitlab ssh url normalized with branch tree" "https://gitlab.com/rootiest/fish-config/-/tree/feat-test" "$url_gl"
|
||||||
|
|
||||||
|
# Test gitea provider normalization
|
||||||
|
git remote set-url origin "https://git.rootiest.dev/rootiest/fish-config.git"
|
||||||
|
git config browse.provider gitea
|
||||||
|
set -l url_gitea (repo-open -p)
|
||||||
|
check "repo-open: gitea url normalized with src/branch" "https://git.rootiest.dev/rootiest/fish-config/src/branch/feat-test" "$url_gitea"
|
||||||
|
|
||||||
|
builtin cd $prev_pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 10. fzf-update (fzf install / git pull)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
section "network isolation: fzf-update"
|
||||||
|
|
||||||
|
reset_mocks
|
||||||
|
# When ~/.fzf exists, git pull fails due to network drop
|
||||||
|
set -l fake_home_fzf (mktemp -d)
|
||||||
|
set -ga TMPDIRS $fake_home_fzf
|
||||||
|
mkdir -p $fake_home_fzf/.fzf
|
||||||
|
|
||||||
|
begin
|
||||||
|
set -lx HOME $fake_home_fzf
|
||||||
|
set -gx MOCK_GIT_FAIL_PULL 1
|
||||||
|
fzf-update >/dev/null 2>&1
|
||||||
|
check "fzf-update: git pull network drop returns non-zero" true (test $status -ne 0; and echo true; or echo false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# When ~/.fzf does not exist, git clone fails due to network drop
|
||||||
|
set -l fake_home_no_fzf (mktemp -d)
|
||||||
|
set -ga TMPDIRS $fake_home_no_fzf
|
||||||
|
|
||||||
|
begin
|
||||||
|
set -lx HOME $fake_home_no_fzf
|
||||||
|
set -gx MOCK_GIT_FAIL_CLONE 1
|
||||||
|
fzf-update >/dev/null 2>&1
|
||||||
|
check "fzf-update: git clone network drop returns non-zero" true (test $status -ne 0; and echo true; or echo false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Teardown and Final Report
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
cleanup
|
||||||
|
report
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# Isolated test suite for Python helper scripts network isolation and edge case mocking.
|
||||||
|
# Exercises scripts/sync-labels.py under offline conditions with full mock coverage.
|
||||||
|
#
|
||||||
|
# Runs isolated (no # MODE: marker, defaulting to isolated).
|
||||||
|
|
||||||
|
source (realpath (dirname (status filename)))/lib.fish
|
||||||
|
|
||||||
|
section "python network isolation: sync-labels.py"
|
||||||
|
|
||||||
|
set -l py_script $repo_root/tests/test_sync_labels.py
|
||||||
|
check "tests/test_sync_labels.py exists" true (test -f $py_script; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l py_lines (python3 $py_script -v 2>&1)
|
||||||
|
set -l py_status $status
|
||||||
|
|
||||||
|
for line in $py_lines
|
||||||
|
if string match -qr '^\s*(test_\w+)\s+\([^)]+\)\s+\.\.\.\s+(\w+)' -- $line
|
||||||
|
set -l match (string match -r '^\s*(test_\w+)\s+\([^)]+\)\s+\.\.\.\s+(\w+)' -- $line)
|
||||||
|
set -l tname $match[2]
|
||||||
|
set -l tres $match[3]
|
||||||
|
check "$tname" ok (string lower $tres)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
check "test_sync_labels.py suite exited 0" 0 $py_status
|
||||||
|
|
||||||
|
report
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# MODE: isolated
|
||||||
|
|
||||||
|
source (realpath (dirname (status filename)))/lib.fish
|
||||||
|
set -p fish_function_path $repo_root/functions
|
||||||
|
set -gx TERM xterm-256color
|
||||||
|
set -g __fish_config_dir $repo_root 2>/dev/null; or true
|
||||||
|
|
||||||
|
# Ensure data/words is reachable via __fish_config_dir in isolated runs
|
||||||
|
if not test -d "$__fish_config_dir/data/words"
|
||||||
|
mkdir -p "$__fish_config_dir/data"
|
||||||
|
ln -sf "$repo_root/data/words" "$__fish_config_dir/data/words"
|
||||||
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# rand_string: Components
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section "rand_string: components"
|
||||||
|
|
||||||
|
check "literal component" FOO (rand_string literal=FOO)
|
||||||
|
check "string component" BAR (rand_string string=BAR)
|
||||||
|
|
||||||
|
set -l out_digits4 (rand_string digits=4)
|
||||||
|
check "digits=4 returns 4 digits" true (string match -qr '^\d{4}$' -- "$out_digits4"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_digits1 (rand_string digits=1)
|
||||||
|
check "digits=1 returns 1 digit" true (string match -qr '^\d$' -- "$out_digits1"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_digits_inv (rand_string digits=abc)
|
||||||
|
check "digits=non-numeric defaults to 1 digit" true (string match -qr '^\d$' -- "$out_digits_inv"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_color (rand_string color)
|
||||||
|
check "category color generates non-empty word" true (test -n "$out_color"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_animal (rand_string animal)
|
||||||
|
check "category animal generates non-empty word" true (test -n "$out_animal"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_noun (rand_string noun)
|
||||||
|
check "category noun generates non-empty word" true (test -n "$out_noun"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_verb (rand_string verb)
|
||||||
|
check "category verb generates non-empty word" true (test -n "$out_verb"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_adj (rand_string adjective)
|
||||||
|
check "category adjective generates non-empty word" true (test -n "$out_adj"; and echo true; or echo false)
|
||||||
|
|
||||||
|
set -l out_name (rand_string name)
|
||||||
|
check "category name generates non-empty word" true (test -n "$out_name"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# rand_string: Separators
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section "rand_string: separators"
|
||||||
|
|
||||||
|
check "default separator is dash" A-B (rand_string literal=A literal=B)
|
||||||
|
check "--separator=underscore" A_B (rand_string --separator=underscore literal=A literal=B)
|
||||||
|
check "--separator=dot" "A.B" (rand_string --separator=dot literal=A literal=B)
|
||||||
|
check "--separator=none" AB (rand_string --separator=none literal=A literal=B)
|
||||||
|
check "--separator=empty" AB (rand_string --separator=empty literal=A literal=B)
|
||||||
|
check "-s dot" "A.B" (rand_string -s dot literal=A literal=B)
|
||||||
|
check "-s underscore" A_B (rand_string -s underscore literal=A literal=B)
|
||||||
|
check "-s none" AB (rand_string -s none literal=A literal=B)
|
||||||
|
check "custom literal separator ::" "A::B" (rand_string --separator=:: literal=A literal=B)
|
||||||
|
|
||||||
|
set -l out_multi (rand_string literal=test --separator=underscore literal=part digits=2)
|
||||||
|
check "multi-component with separators" true (string match -qr '^test_part_\d{2}$' -- "$out_multi"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# rand_string: Casing
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section "rand_string: casing"
|
||||||
|
|
||||||
|
check "--case=upper produces uppercase" true (string match -qr '^[A-Z]+$' -- (rand_string --case=upper color); and echo true; or echo false)
|
||||||
|
check "--case=lower produces lowercase" true (string match -qr '^[a-z]+$' -- (rand_string --case=lower color); and echo true; or echo false)
|
||||||
|
check "--case=title produces titlecase" true (string match -qr '^[A-Z][a-z]+$' -- (rand_string --case=title color); and echo true; or echo false)
|
||||||
|
|
||||||
|
check "-c upper flag" true (string match -qr '^[A-Z]+$' -- (rand_string -c upper color); and echo true; or echo false)
|
||||||
|
check "-c lower flag" true (string match -qr '^[a-z]+$' -- (rand_string -c lower color); and echo true; or echo false)
|
||||||
|
check "-c title flag" true (string match -qr '^[A-Z][a-z]+$' -- (rand_string -c title color); and echo true; or echo false)
|
||||||
|
|
||||||
|
check "literal preserves casing with --case=upper" foo (rand_string --case=upper literal=foo)
|
||||||
|
check "literal preserves casing with --case=lower" FOO (rand_string --case=lower literal=FOO)
|
||||||
|
check "literal preserves casing with --case=title" foo (rand_string --case=title literal=foo)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# rand_string: Errors and Help
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section "rand_string: errors and help"
|
||||||
|
|
||||||
|
rand_string nonexistent_cat_xyz >/dev/null 2>&1
|
||||||
|
check "missing category returns 1" 1 $status
|
||||||
|
|
||||||
|
set -l err_msg (rand_string nonexistent_cat_xyz 2>&1 >/dev/null)
|
||||||
|
check "missing category reports error on stderr" true (string match -qr 'unknown category' -- "$err_msg"; and echo true; or echo false)
|
||||||
|
|
||||||
|
rand_string -h >/dev/null 2>&1
|
||||||
|
check "-h returns 0" 0 $status
|
||||||
|
|
||||||
|
set -l help_h (rand_string -h)
|
||||||
|
check "-h displays usage" true (string match -qr 'Usage:' -- "$help_h"; and echo true; or echo false)
|
||||||
|
|
||||||
|
rand_string --help >/dev/null 2>&1
|
||||||
|
check "--help returns 0" 0 $status
|
||||||
|
|
||||||
|
set -l help_long (rand_string --help)
|
||||||
|
check "--help displays usage" true (string match -qr 'Usage:' -- "$help_long"; and echo true; or echo false)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Mock commandline infrastructure
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
set -g mock_cmd_buffer ""
|
||||||
|
set -g mock_cmd_cursor 0
|
||||||
|
set -g mock_cmd_token ""
|
||||||
|
set -g mock_cmd_inserted ""
|
||||||
|
set -g mock_cmd_in_search 0
|
||||||
|
|
||||||
|
function commandline
|
||||||
|
if test (count $argv) -eq 0
|
||||||
|
echo -n "$mock_cmd_buffer"
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
switch $argv[1]
|
||||||
|
case -C
|
||||||
|
if test (count $argv) -ge 2
|
||||||
|
set -g mock_cmd_cursor $argv[2]
|
||||||
|
else
|
||||||
|
echo -n "$mock_cmd_cursor"
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
case -r
|
||||||
|
if test (count $argv) -ge 2
|
||||||
|
set -g mock_cmd_buffer "$argv[2]"
|
||||||
|
else
|
||||||
|
set -g mock_cmd_buffer ""
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
case --
|
||||||
|
if test (count $argv) -ge 2
|
||||||
|
set -g mock_cmd_buffer "$argv[2]"
|
||||||
|
else
|
||||||
|
set -g mock_cmd_buffer ""
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
case -i --insert
|
||||||
|
if test (count $argv) -ge 2
|
||||||
|
set -g mock_cmd_inserted "$argv[2]"
|
||||||
|
set -g mock_cmd_buffer "$mock_cmd_buffer$argv[2]"
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
case --search-field
|
||||||
|
if contains -- --insert $argv
|
||||||
|
set -l idx (contains -i -- --insert $argv)
|
||||||
|
set -l val $argv[(math $idx + 1)]
|
||||||
|
set -g mock_cmd_inserted "$val"
|
||||||
|
set -g mock_cmd_buffer "$mock_cmd_buffer$val"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
test "$mock_cmd_in_search" -eq 1
|
||||||
|
return $status
|
||||||
|
end
|
||||||
|
case -t --current-token
|
||||||
|
if test (count $argv) -ge 2
|
||||||
|
set -l old_tok "$mock_cmd_token"
|
||||||
|
set -g mock_cmd_token "$argv[2]"
|
||||||
|
if test -n "$old_tok"
|
||||||
|
set -g mock_cmd_buffer (string replace -- "$old_tok" "$argv[2]" "$mock_cmd_buffer")
|
||||||
|
else
|
||||||
|
set -g mock_cmd_buffer "$mock_cmd_buffer$argv[2]"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo -n "$mock_cmd_token"
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set -g _test_hist_files
|
||||||
|
|
||||||
|
function set_test_history --argument-names cmd
|
||||||
|
set -l session "test_hist_"(random)
|
||||||
|
set -l hist_dir ""
|
||||||
|
if test -n "$XDG_DATA_HOME"
|
||||||
|
set hist_dir "$XDG_DATA_HOME/fish"
|
||||||
|
else
|
||||||
|
set hist_dir "$HOME/.local/share/fish"
|
||||||
|
end
|
||||||
|
mkdir -p "$hist_dir"
|
||||||
|
set -l hist_file "$hist_dir/"$session"_history"
|
||||||
|
echo "- cmd: $cmd" >"$hist_file"
|
||||||
|
echo " when: "(date +%s) >>"$hist_file"
|
||||||
|
set -g fish_history "$session"
|
||||||
|
set -ga _test_hist_files "$hist_file"
|
||||||
|
end
|
||||||
|
|
||||||
|
function cleanup_test_history
|
||||||
|
if set -q _test_hist_files
|
||||||
|
for f in $_test_hist_files
|
||||||
|
if test -f "$f"
|
||||||
|
rm -f "$f"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set -e _test_hist_files
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# _replace_command_token
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section _replace_command_token
|
||||||
|
|
||||||
|
set mock_cmd_buffer "git status"
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "git status: buffer replaces command token" " status" "$mock_cmd_buffer"
|
||||||
|
check "git status: cursor placed at 0" 0 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
set mock_cmd_buffer "sudo rm -rf /tmp"
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "sudo rm -rf /tmp: buffer preserves sudo and replaces command" "sudo -rf /tmp" "$mock_cmd_buffer"
|
||||||
|
check "sudo rm -rf /tmp: cursor placed at 5" 5 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
set mock_cmd_buffer "sudo systemctl status nginx"
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "sudo systemctl status: buffer preserves sudo" "sudo status nginx" "$mock_cmd_buffer"
|
||||||
|
check "sudo systemctl status: cursor placed at 5" 5 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
set mock_cmd_buffer "python -m unittest"
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "python -m unittest: buffer replaces command token" " -m unittest" "$mock_cmd_buffer"
|
||||||
|
check "python -m unittest: cursor placed at 0" 0 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
set mock_cmd_buffer ls
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "single token ls: buffer becomes single space" " " "$mock_cmd_buffer"
|
||||||
|
check "single token ls: cursor placed at 0" 0 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
set mock_cmd_buffer "sudo reboot"
|
||||||
|
set mock_cmd_cursor -1
|
||||||
|
_replace_command_token
|
||||||
|
check "sudo reboot: buffer becomes sudo with two spaces" "sudo " "$mock_cmd_buffer"
|
||||||
|
check "sudo reboot: cursor placed at 5" 5 "$mock_cmd_cursor"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# __substitute_typo
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section __substitute_typo
|
||||||
|
|
||||||
|
set_test_history 'git commit -m "feat"'
|
||||||
|
set mock_cmd_buffer "^feat^fix"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "substitute ^feat^fix in git commit" 'git commit -m "fix"' "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set_test_history "docker run -d nginx"
|
||||||
|
set mock_cmd_buffer "^nginx^redis"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "substitute ^nginx^redis in docker run" "docker run -d redis" "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set_test_history "echo foo foo"
|
||||||
|
set mock_cmd_buffer "^foo^bar"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "substitute replaces all occurrences" "echo bar bar" "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set_test_history "git checkout main feat"
|
||||||
|
set mock_cmd_buffer "^feat^"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "substitute ^feat^ with empty string deletes token" "git checkout main " "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set_test_history "git status"
|
||||||
|
set mock_cmd_buffer "^"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "lone caret inserts literal caret" "^" "$mock_cmd_inserted"
|
||||||
|
check "lone caret buffer retains caret" "^^" "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set_test_history "git status"
|
||||||
|
set mock_cmd_buffer "git log"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
__substitute_typo
|
||||||
|
check "non-matching buffer inserts literal caret" "^" "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
cleanup_test_history
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# _puffer_fish_expand_dot
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section _puffer_fish_expand_dot
|
||||||
|
|
||||||
|
set mock_cmd_token ".."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token .. inserts /.." "/.." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token "../.."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token ../.. inserts /.." "/.." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token "../../.."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token ../../.. inserts /.." "/.." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token "."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token . inserts ." "." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token "..."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token ... inserts ." "." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token foo
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "token foo inserts ." "." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token ".."
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_in_search 1
|
||||||
|
_puffer_fish_expand_dot
|
||||||
|
check "search field mode inserts . even if token is .." "." "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# _puffer_fish_expand_bang
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
section _puffer_fish_expand_bang
|
||||||
|
|
||||||
|
set_test_history "cargo test --release"
|
||||||
|
|
||||||
|
set mock_cmd_token "!"
|
||||||
|
set mock_cmd_buffer "!"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_bang
|
||||||
|
check "token ! expands to history[1]" "cargo test --release" "$mock_cmd_token"
|
||||||
|
check "token ! updates buffer" "cargo test --release" "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set mock_cmd_token "!"
|
||||||
|
set mock_cmd_buffer "sudo !"
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_bang
|
||||||
|
check "sudo ! expands token to history[1]" "sudo cargo test --release" "$mock_cmd_buffer"
|
||||||
|
|
||||||
|
set mock_cmd_token git
|
||||||
|
set mock_cmd_buffer git
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_bang
|
||||||
|
check "non-bang token inserts literal !" "!" "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token ""
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_in_search 0
|
||||||
|
_puffer_fish_expand_bang
|
||||||
|
check "empty token inserts literal !" "!" "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
set mock_cmd_token "!"
|
||||||
|
set mock_cmd_buffer ""
|
||||||
|
set mock_cmd_inserted ""
|
||||||
|
set mock_cmd_in_search 1
|
||||||
|
_puffer_fish_expand_bang
|
||||||
|
check "search field mode inserts literal !" "!" "$mock_cmd_inserted"
|
||||||
|
|
||||||
|
cleanup_test_history
|
||||||
|
|
||||||
|
# Unshadow commandline builtin
|
||||||
|
functions -e commandline
|
||||||
|
|
||||||
|
report
|
||||||
@@ -0,0 +1,429 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Comprehensive unit tests for scripts/sync-labels.py with network isolation."""
|
||||||
|
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import warnings
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch, call
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
# Load scripts/sync-labels.py via importlib (handling the hyphen in filename)
|
||||||
|
_script_path = Path(__file__).resolve().parent.parent / "scripts" / "sync-labels.py"
|
||||||
|
_spec = importlib.util.spec_from_file_location("sync_labels", _script_path)
|
||||||
|
sl = importlib.util.module_from_spec(_spec)
|
||||||
|
sys.modules["sync_labels"] = sl
|
||||||
|
_spec.loader.exec_module(sl)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_http_error(url, code, msg, headers, body_bytes):
|
||||||
|
fp = io.BytesIO(body_bytes)
|
||||||
|
return urllib.error.HTTPError(url, code, msg, headers, fp)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequestNetworkDrop(unittest.TestCase):
|
||||||
|
"""Edge cases for complete network drop and connection failures."""
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_connection_refused(self, mock_urlopen):
|
||||||
|
mock_urlopen.side_effect = urllib.error.URLError(
|
||||||
|
ConnectionRefusedError(111, "Connection refused")
|
||||||
|
)
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("Connection refused", str(ctx.exception))
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_dns_resolution_failure(self, mock_urlopen):
|
||||||
|
mock_urlopen.side_effect = urllib.error.URLError(
|
||||||
|
socket.gaierror(-2, "Name or service not known")
|
||||||
|
)
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://nonexistent.invalid/test")
|
||||||
|
self.assertIn("Name or service not known", str(ctx.exception))
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_network_unreachable(self, mock_urlopen):
|
||||||
|
mock_urlopen.side_effect = urllib.error.URLError(
|
||||||
|
OSError(101, "Network is unreachable")
|
||||||
|
)
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("Network is unreachable", str(ctx.exception))
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequestTimeouts(unittest.TestCase):
|
||||||
|
"""Edge cases for slow or hung connections and timeouts."""
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_socket_timeout(self, mock_urlopen):
|
||||||
|
mock_urlopen.side_effect = urllib.error.URLError(
|
||||||
|
socket.timeout("The read operation timed out")
|
||||||
|
)
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("timed out", str(ctx.exception))
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_timeout_parameter_passed(self, mock_urlopen):
|
||||||
|
mock_resp = MagicMock()
|
||||||
|
mock_resp.read.return_value = b'{"ok": true}'
|
||||||
|
mock_resp.__enter__.return_value = mock_resp
|
||||||
|
mock_resp.__exit__.return_value = None
|
||||||
|
mock_urlopen.return_value = mock_resp
|
||||||
|
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
mock_urlopen.assert_called_once()
|
||||||
|
_, kwargs = mock_urlopen.call_args
|
||||||
|
self.assertEqual(kwargs.get("timeout"), sl.TIMEOUT)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequestHttpCodes(unittest.TestCase):
|
||||||
|
"""Edge cases for HTTP 4xx and 5xx responses, rate limiting, and errors."""
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_http_401_unauthorized(self, mock_urlopen):
|
||||||
|
err = _make_http_error(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
401,
|
||||||
|
"Unauthorized",
|
||||||
|
{},
|
||||||
|
b'{"message": "Bad credentials"}',
|
||||||
|
)
|
||||||
|
mock_urlopen.side_effect = err
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test", token="invalid")
|
||||||
|
self.assertIn("HTTP 401", str(ctx.exception))
|
||||||
|
self.assertIn("Bad credentials", str(ctx.exception))
|
||||||
|
err.close()
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_http_403_rate_limit(self, mock_urlopen):
|
||||||
|
err = _make_http_error(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
403,
|
||||||
|
"Forbidden",
|
||||||
|
{"X-RateLimit-Remaining": "0"},
|
||||||
|
b'{"message": "API rate limit exceeded for user"}',
|
||||||
|
)
|
||||||
|
mock_urlopen.side_effect = err
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("HTTP 403", str(ctx.exception))
|
||||||
|
self.assertIn("rate limit exceeded", str(ctx.exception))
|
||||||
|
err.close()
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_http_404_not_found(self, mock_urlopen):
|
||||||
|
err = _make_http_error(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
404,
|
||||||
|
"Not Found",
|
||||||
|
{},
|
||||||
|
b'{"message": "Not Found"}',
|
||||||
|
)
|
||||||
|
mock_urlopen.side_effect = err
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("HTTP 404", str(ctx.exception))
|
||||||
|
err.close()
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_http_500_server_error(self, mock_urlopen):
|
||||||
|
err = _make_http_error(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
500,
|
||||||
|
"Internal Server Error",
|
||||||
|
{},
|
||||||
|
b"Internal Server Error",
|
||||||
|
)
|
||||||
|
mock_urlopen.side_effect = err
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("HTTP 500", str(ctx.exception))
|
||||||
|
err.close()
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_http_502_bad_gateway(self, mock_urlopen):
|
||||||
|
err = _make_http_error(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
502,
|
||||||
|
"Bad Gateway",
|
||||||
|
{},
|
||||||
|
b"<html><body>502 Bad Gateway</body></html>",
|
||||||
|
)
|
||||||
|
mock_urlopen.side_effect = err
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
self.assertIn("HTTP 502", str(ctx.exception))
|
||||||
|
err.close()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequestPayloads(unittest.TestCase):
|
||||||
|
"""Edge cases for payloads, parsing, and request formatting."""
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_valid_json(self, mock_urlopen):
|
||||||
|
mock_resp = MagicMock()
|
||||||
|
mock_resp.read.return_value = b'[{"name": "Kind/Bug", "color": "ee0701"}]'
|
||||||
|
mock_resp.__enter__.return_value = mock_resp
|
||||||
|
mock_resp.__exit__.return_value = None
|
||||||
|
mock_urlopen.return_value = mock_resp
|
||||||
|
|
||||||
|
res = sl.request("https://api.github.com/test")
|
||||||
|
self.assertEqual(res, [{"name": "Kind/Bug", "color": "ee0701"}])
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_empty_body_204(self, mock_urlopen):
|
||||||
|
mock_resp = MagicMock()
|
||||||
|
mock_resp.read.return_value = b""
|
||||||
|
mock_resp.__enter__.return_value = mock_resp
|
||||||
|
mock_resp.__exit__.return_value = None
|
||||||
|
mock_urlopen.return_value = mock_resp
|
||||||
|
|
||||||
|
res = sl.request("https://api.github.com/test", method="DELETE")
|
||||||
|
self.assertIsNone(res)
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_headers_and_auth(self, mock_urlopen):
|
||||||
|
mock_resp = MagicMock()
|
||||||
|
mock_resp.read.return_value = b"{}"
|
||||||
|
mock_resp.__enter__.return_value = mock_resp
|
||||||
|
mock_resp.__exit__.return_value = None
|
||||||
|
mock_urlopen.return_value = mock_resp
|
||||||
|
|
||||||
|
sl.request(
|
||||||
|
"https://api.github.com/test",
|
||||||
|
token="secret_token",
|
||||||
|
method="POST",
|
||||||
|
payload={"name": "test"},
|
||||||
|
)
|
||||||
|
req = mock_urlopen.call_args[0][0]
|
||||||
|
self.assertEqual(req.get_header("Authorization"), "Bearer secret_token")
|
||||||
|
self.assertEqual(req.get_header("Accept"), "application/json")
|
||||||
|
self.assertEqual(req.get_header("Content-type"), "application/json")
|
||||||
|
self.assertEqual(req.get_method(), "POST")
|
||||||
|
self.assertEqual(req.data, b'{"name": "test"}')
|
||||||
|
|
||||||
|
@patch("urllib.request.urlopen")
|
||||||
|
def test_request_corrupted_json_payload(self, mock_urlopen):
|
||||||
|
mock_resp = MagicMock()
|
||||||
|
mock_resp.read.return_value = b'{"name": "broken", invalid_json'
|
||||||
|
mock_resp.__enter__.return_value = mock_resp
|
||||||
|
mock_resp.__exit__.return_value = None
|
||||||
|
mock_urlopen.return_value = mock_resp
|
||||||
|
|
||||||
|
with self.assertRaises(json.JSONDecodeError):
|
||||||
|
sl.request("https://api.github.com/test")
|
||||||
|
|
||||||
|
|
||||||
|
class TestPaginate(unittest.TestCase):
|
||||||
|
"""Edge cases for pagination across API responses."""
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_paginate_single_page(self, mock_req):
|
||||||
|
mock_req.return_value = [{"name": "A"}, {"name": "B"}]
|
||||||
|
res = sl.paginate("https://api.test?page={page}&per_page={per_page}", per_page=50)
|
||||||
|
self.assertEqual(len(res), 2)
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
"https://api.test?page=1&per_page=50", token=None
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_paginate_multiple_pages(self, mock_req):
|
||||||
|
page1 = [{"name": f"item{i}"} for i in range(50)]
|
||||||
|
page2 = [{"name": f"item{i}"} for i in range(50, 70)]
|
||||||
|
mock_req.side_effect = [page1, page2]
|
||||||
|
|
||||||
|
res = sl.paginate("https://api.test?page={page}&per_page={per_page}", per_page=50)
|
||||||
|
self.assertEqual(len(res), 70)
|
||||||
|
self.assertEqual(mock_req.call_count, 2)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_paginate_exact_page_boundary(self, mock_req):
|
||||||
|
page1 = [{"name": f"item{i}"} for i in range(50)]
|
||||||
|
page2 = []
|
||||||
|
mock_req.side_effect = [page1, page2]
|
||||||
|
|
||||||
|
res = sl.paginate("https://api.test?page={page}&per_page={per_page}", per_page=50)
|
||||||
|
self.assertEqual(len(res), 50)
|
||||||
|
self.assertEqual(mock_req.call_count, 2)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_paginate_empty(self, mock_req):
|
||||||
|
mock_req.return_value = []
|
||||||
|
res = sl.paginate("https://api.test?page={page}&per_page={per_page}", per_page=50)
|
||||||
|
self.assertEqual(res, [])
|
||||||
|
mock_req.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class TestLabelOperations(unittest.TestCase):
|
||||||
|
"""Edge cases for label CRUD operations and usage querying."""
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_usage_count_zero(self, mock_req):
|
||||||
|
mock_req.return_value = []
|
||||||
|
count = sl.usage_count("Unused Label", "token")
|
||||||
|
self.assertEqual(count, 0)
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
f"{sl.GITHUB_API}/repos/{sl.GITHUB_REPO}/issues"
|
||||||
|
f"?labels=Unused%20Label&state=all&per_page=100",
|
||||||
|
token="token",
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_usage_count_active(self, mock_req):
|
||||||
|
mock_req.return_value = [{"number": 1}, {"number": 2}]
|
||||||
|
count = sl.usage_count("Priority/High", "token")
|
||||||
|
self.assertEqual(count, 2)
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
f"{sl.GITHUB_API}/repos/{sl.GITHUB_REPO}/issues"
|
||||||
|
f"?labels=Priority%2FHigh&state=all&per_page=100",
|
||||||
|
token="token",
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_create_label(self, mock_req):
|
||||||
|
label = {"name": "Test/Label", "color": "123456", "description": "Desc"}
|
||||||
|
sl.create_label(label, "tok")
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
f"{sl.GITHUB_API}/repos/{sl.GITHUB_REPO}/labels",
|
||||||
|
token="tok",
|
||||||
|
method="POST",
|
||||||
|
payload=label,
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_update_label(self, mock_req):
|
||||||
|
label = {"name": "Area/Prompt & Theme", "color": "abcdef", "description": "New"}
|
||||||
|
sl.update_label(label, "tok")
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
f"{sl.GITHUB_API}/repos/{sl.GITHUB_REPO}/labels/Area%2FPrompt%20%26%20Theme",
|
||||||
|
token="tok",
|
||||||
|
method="PATCH",
|
||||||
|
payload={
|
||||||
|
"new_name": "Area/Prompt & Theme",
|
||||||
|
"color": "abcdef",
|
||||||
|
"description": "New",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("sync_labels.request")
|
||||||
|
def test_delete_label(self, mock_req):
|
||||||
|
sl.delete_label("Reviewed/Won't Fix", "tok")
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
f"{sl.GITHUB_API}/repos/{sl.GITHUB_REPO}/labels/Reviewed%2FWon%27t%20Fix",
|
||||||
|
token="tok",
|
||||||
|
method="DELETE",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunAndMainWorkflow(unittest.TestCase):
|
||||||
|
"""End-to-end execution testing dry-run, live execution, and error handling."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.env_patcher = patch.dict(os.environ, {}, clear=True)
|
||||||
|
self.env_patcher.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.env_patcher.stop()
|
||||||
|
|
||||||
|
def test_run_missing_token_not_dry_run_raises(self):
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.run(dry_run=False)
|
||||||
|
self.assertIn("GH_MIRROR_TOKEN is not set", str(ctx.exception))
|
||||||
|
|
||||||
|
@patch("sync_labels.fetch_gitea_labels")
|
||||||
|
def test_run_gitea_empty_raises(self, mock_gitea):
|
||||||
|
os.environ[sl.TOKEN_ENV] = "dummy_token"
|
||||||
|
mock_gitea.return_value = []
|
||||||
|
with self.assertRaises(sl.SyncError) as ctx:
|
||||||
|
sl.run(dry_run=False)
|
||||||
|
self.assertIn("Gitea returned no labels", str(ctx.exception))
|
||||||
|
|
||||||
|
@patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
@patch("sync_labels.delete_label")
|
||||||
|
@patch("sync_labels.update_label")
|
||||||
|
@patch("sync_labels.create_label")
|
||||||
|
@patch("sync_labels.usage_count")
|
||||||
|
@patch("sync_labels.fetch_github_labels")
|
||||||
|
@patch("sync_labels.fetch_gitea_labels")
|
||||||
|
def test_run_dry_run_makes_no_mutating_calls(
|
||||||
|
self, mock_gitea, mock_gh, mock_usage, mock_create, mock_update, mock_delete, mock_stdout
|
||||||
|
):
|
||||||
|
mock_gitea.return_value = [
|
||||||
|
{"name": "Keep", "color": "111111", "description": "Same"},
|
||||||
|
{"name": "New", "color": "222222", "description": "Created"},
|
||||||
|
{"name": "Change", "color": "333333", "description": "Updated"},
|
||||||
|
]
|
||||||
|
mock_gh.return_value = [
|
||||||
|
{"name": "Keep", "color": "111111", "description": "Same"},
|
||||||
|
{"name": "Change", "color": "000000", "description": "Old"},
|
||||||
|
{"name": "ExtraUnused", "color": "444444", "description": "Deleted"},
|
||||||
|
{"name": "ExtraUsed", "color": "555555", "description": "Kept"},
|
||||||
|
]
|
||||||
|
mock_usage.side_effect = lambda name, token: 1 if name == "ExtraUsed" else 0
|
||||||
|
|
||||||
|
# In dry run, token is not strictly required
|
||||||
|
status = sl.run(dry_run=True)
|
||||||
|
self.assertEqual(status, 0)
|
||||||
|
mock_create.assert_not_called()
|
||||||
|
mock_update.assert_not_called()
|
||||||
|
mock_delete.assert_not_called()
|
||||||
|
|
||||||
|
@patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
@patch("sync_labels.delete_label")
|
||||||
|
@patch("sync_labels.update_label")
|
||||||
|
@patch("sync_labels.create_label")
|
||||||
|
@patch("sync_labels.usage_count")
|
||||||
|
@patch("sync_labels.fetch_github_labels")
|
||||||
|
@patch("sync_labels.fetch_gitea_labels")
|
||||||
|
def test_run_live_executes_plan(
|
||||||
|
self, mock_gitea, mock_gh, mock_usage, mock_create, mock_update, mock_delete, mock_stdout
|
||||||
|
):
|
||||||
|
os.environ[sl.TOKEN_ENV] = "my_token"
|
||||||
|
mock_gitea.return_value = [
|
||||||
|
{"name": "Keep", "color": "111111", "description": "Same"},
|
||||||
|
{"name": "New", "color": "222222", "description": "Created"},
|
||||||
|
{"name": "Change", "color": "333333", "description": "Updated"},
|
||||||
|
]
|
||||||
|
mock_gh.return_value = [
|
||||||
|
{"name": "Keep", "color": "111111", "description": "Same"},
|
||||||
|
{"name": "Change", "color": "000000", "description": "Old"},
|
||||||
|
{"name": "ExtraUnused", "color": "444444", "description": "Deleted"},
|
||||||
|
{"name": "ExtraUsed", "color": "555555", "description": "Kept"},
|
||||||
|
]
|
||||||
|
mock_usage.side_effect = lambda name, token: 1 if name == "ExtraUsed" else 0
|
||||||
|
|
||||||
|
status = sl.run(dry_run=False)
|
||||||
|
self.assertEqual(status, 0)
|
||||||
|
mock_create.assert_called_once_with(
|
||||||
|
{"name": "New", "color": "222222", "description": "Created"}, "my_token"
|
||||||
|
)
|
||||||
|
mock_update.assert_called_once()
|
||||||
|
mock_delete.assert_called_once_with("ExtraUnused", "my_token")
|
||||||
|
|
||||||
|
@patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
def test_main_self_test(self, mock_stdout):
|
||||||
|
self.assertEqual(sl.main(["--self-test"]), 0)
|
||||||
|
|
||||||
|
@patch("sync_labels.run")
|
||||||
|
def test_main_sync_error_exit_code(self, mock_run):
|
||||||
|
mock_run.side_effect = sl.SyncError("Boom")
|
||||||
|
with patch("sys.stderr", new=io.StringIO()) as fake_err:
|
||||||
|
rc = sl.main(["--dry-run"])
|
||||||
|
self.assertEqual(rc, 1)
|
||||||
|
self.assertIn("error: Boom", fake_err.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user