nushell-config/modules/ssh.nu
2024-02-18 22:52:08 -06:00

136 lines
4.1 KiB
Plaintext

export def ensure-cache [cache paths action] {
mut cfgs = []
for i in $paths {
let cs = (do -i {ls $i})
if not ($cs | is-empty) {
$cfgs = ($cfgs | append $cs)
}
}
let cfgs = $cfgs
let ts = ($cfgs | sort-by modified | reverse | get 0.modified)
if ($ts | is-empty) { return false }
let tc = (do -i { ls $cache | get 0.modified })
if not (($cache | path exists) and ($ts < $tc)) {
mkdir ($cache | path dirname)
do $action | save -f $cache
}
open $cache
}
export def 'str max-length' [] {
$in | reduce -f 0 {|x, a|
if ($x|is-empty) { return $a }
let l = ($x | str length)
if $l > $a { $l } else { $a }
}
}
def "nu-complete ssh host" [] {
rg -LNI '^Host [a-z0-9_\-\.]+' ~/.ssh | lines | each {|x| $x | split row ' '| get 1}
}
export def parse-ssh-file [group] {
$in
| parse -r '(?P<k>Host|HostName|User|Port|IdentityFile)\s+(?P<v>.+)'
| append { k: Host, v: null}
| reduce -f { rst: [], item: {Host: null} } {|it, acc|
if $it.k == 'Host' {
$acc | upsert rst ($acc.rst | append $acc.item)
| upsert item { Host : $it.v, HostName: null, Port: null, User: null, IdentityFile: null, Group: $group }
} else {
$acc | upsert item ($acc.item | upsert $it.k $it.v)
}
}
| get rst
| where {|x| not (($x.Host | is-empty) or $x.Host =~ '\*')}
}
export def ssh-list [] {
rg -L -l 'Host' ~/.ssh
| lines
| each {|x| cat $x | parse-ssh-file $x}
| flatten
}
def fmt-group [p] {
$p | str replace $"($env.HOME)/.ssh/" ''
}
def "ssh-hosts" [] {
let cache = $'($env.HOME)/.cache/nu-complete/ssh.json'
ensure-cache $cache [~/.ssh/config ~/.ssh/config*/* ] { ||
let data = (ssh-list | each {|x|
let uri = $"($x.User)@($x.HostName):($x.Port)"
{
value: $x.Host,
uri: $uri,
group: $"(fmt-group $x.Group)",
identfile: $"($x.IdentityFile)",
}
})
let max = {
value: ($data.value | str max-length),
uri: ($data.uri | str max-length),
group: ($data.group | str max-length),
identfile: ($data.identfile | str max-length),
}
{max: $max, completion: $data}
}
}
def "nu-complete ssh" [] {
let data = (ssh-hosts)
$data.completion
| each { |x|
let uri = ($x.uri | fill -a l -w $data.max.uri -c ' ')
let group = ($x.group | fill -a l -w $data.max.group -c ' ')
let id = ($x.identfile | fill -a l -w $data.max.identfile -c ' ')
{value: $x.value, description: $"\t($uri) ($group) ($id)" }
}
}
export extern main [
host: string@"nu-complete ssh" # host
...cmd # cmd
-v # verbose
-i: string # key
-p: int # port
-N # n
-T # t
-L # l
-R # r
-D # d
-J: string # j
-W: string # w
]
def "nu-complete scp" [cmd: string, offset: int] {
let argv = ($cmd | str substring ..$offset | split row ' ')
let p = if ($argv | length) > 2 { $argv | get 2 } else { $argv | get 1 }
let ssh = (ssh-hosts | get completion
| each {|x| {value: $"($x.value):" description: $x.uri} }
)
let n = ($p | split row ':')
if $"($n | get 0):" in ($ssh | get value) {
^ssh ($n | get 0) $"sh -c 'ls -dp ($n | get 1)*'"
| lines
| each {|x| $"($n | get 0):($x)"}
} else {
let files = (do -i {
ls -a $"($p)*"
| each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }}
})
$files | append $ssh
}
}
export def scp [
lhs: string@"nu-complete scp",
rhs: string@"nu-complete scp"
] {
^scp -r $lhs $rhs
}