1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-08-01 06:37:46 +00:00

kubernetes completion of jsonpath (#487)

* nu-complete kube jsonpath

The behavior is correct, but the completion menu cannot be displayed

* jsonpath completion for list

* New nushell-style git.nu

Original renamed to git-classic.nu

See `git.md` for details

* rename git.md to README.md

* Move the helper function to the back

* revert power_kube ensure-cache

* A more precise description of `gp`

* description of behavior that both ahead and behind exist

* clean up redundant alias

git show can use gl <hash> instead (hash can be completed)

* add description of `gl`

* add description of gp, ga, gd's parameter

* typo: gpc to gcp

* description of `gp --override`

* rename git.nu to git-v2.nu and git_classic.nu to git.nu

* `git fetch` as fallback of `gp` and temporarily disable `gstat`

temporarily disable `gstat` in `power_git` because:

- `gstat` is not much faster than nushell's implementation of `git_status`
- Sometimes the information displayed is inaccurate, but `git_status` parses the output of `git`, which is more reliable
- No need to load additional plugins

---------

Co-authored-by: agent <agent@nuc>
Co-authored-by: nash <nash@iffy.me>
This commit is contained in:
fj0r 2023-05-13 23:59:30 +08:00 committed by GitHub
parent b3ae6f4af0
commit 632a62f035
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 646 additions and 27 deletions

View file

@ -1,3 +1,79 @@
# Git branch cleanup
Consider putting related commands together, such as `push` and `pull`,
to decide how to execute based on the current environment or specified parameters.
Remove any local git branch no longer being tracked in origin / remote repo.
This mode is more convenient,
but more difficult to implement in traditional shells such as zsh.
In nushell it's very easy.
In this way, the commands that need to be memorized will be greatly reduced,
and the parameters can be automatically completed.
In the most common scenarios, it is even possible to omit the parameter.
In contrast, the behavior of commands requires fine-tuning to be intuitive.
(there may still be some unreasonable places that need to be adjusted)
I tried to tidy part of it, and it is basically usable.
(The goal is to organize all aliases to where they should go)
### gs
Git status and stash.
### gl
Git log and show.
### gb
About the branch.
Create branch if it doesn't exist,
switch otherwise (branch are automatically completed),
use -d to delete.
If there is no branch as an argument, the branch is displayed.
### gp
Pull, push and other related to remote repositories
- `--clone` to clone
- `--submodule` submodule update and submodule init (with `--init`)
- `--force` push --force (assume `pull --force` doesn't make sense)
- `--init` git init
- `--override` just used to trigger a github actions event (in fact, webhooks can also be used)
- if branch is specified, we assume it is `git fetch`
- unless -u is specified: `git push -u`
- finally, if no branch and above parameters are specified, `git pull` or `git push` will be executed according to the current state.
- if both `ahead` and `behind` exist, only `pull`
### ga
Git add, rm and restore. about files.
Use `ga` because git add has the highest execution frequency,
and `ga` is the most convenient input.
- `--delete` git rm
- `--restore` git restore
- git add
### gc
Git commit
### gd
Git diff
### gm
Git merge and rebase
- `--rebase` git rebase
- git merge
### gcp
Git cherry-pick
### gr
Git reset
### grmt
Git remote
### gbs
Git bisect

510
modules/git/git-v2.nu Normal file
View file

@ -0,0 +1,510 @@
def agree [prompt] {
let prompt = if ($prompt | str ends-with '!') {
$'(ansi red)($prompt)(ansi reset) '
} else {
$'($prompt) '
}
(input $prompt | str downcase) in ['y', 'yes', 'ok', 't', 'true', '1']
}
# git status and stash
export def gs [
--apply (-a): bool
--clear (-c): bool
--drop (-d): bool
--list (-l): bool
--pop (-p): bool
--show (-s): bool
--all (-A): bool
--include-untracked (-i): bool
] {
if $apply {
git stash apply
} else if $clear {
git stash clear
} else if $drop {
git stash drop
} else if $list {
git stash list
} else if $pop {
git stash pop
} else if $show {
git stash show --text
} else if $all {
let iu = if $include_untracked { [--include-untracked] } else { [] }
git stash --all $iu
} else {
git status
}
}
# git log
export def gl [
commit?: string@"nu-complete git log"
--verbose(-v):bool
--num(-n):int=32
] {
if ($commit|is-empty) {
_git_log $verbose $num
} else {
git log --stat -p -n 1 $commit
}
}
# git branch
export def gb [
branch?: string@"nu-complete git branches"
--delete (-d): bool
--remote (-r): bool
--no-merged (-n): bool
] {
let bs = (git branch | lines | each {|x| $x | str substring 2..})
if ($branch | is-empty) {
if $remote {
git branch --remote
} else if $no_merged {
git branch --no-merged
} else {
git branch
}
} else if $branch in $bs {
if $delete {
if (agree 'branch will be delete!') {
git branch -D $branch
}
} else {
git checkout $branch
}
} else {
if (agree 'create new branch?') {
git checkout -b $branch
}
}
}
# git pull, push and switch
export def gp [
branch?: string@"nu-complete git branches"
remote?: string@"nu-complete git remotes"
--force (-f): bool # git push -f
--set-upstream (-u): bool # git push -u
--override: bool
--clone (-c): string # git clone
--submodule (-s): bool # git submodule
--init (-i): bool # git init
--rebase (-r): bool # git pull --rebase
--autostash (-a): bool # git pull --autostash
] {
if not ($clone | is-empty) {
let s = if $submodule { [--recurse-submodules] } else { [] }
git clone $s $clone
} else if $submodule {
if $init {
git submodule init
} else {
git submodule update
}
} else if $init {
let repo = $branch
git init $repo
} else if $force {
git push --force
} else if $override {
git pull
git add --all
git commit -v -a --no-edit --amend
git push --force
} else if not ($branch | is-empty) {
let remote = if ($remote|is-empty) { 'origin' } else { $remote }
if $set_upstream {
git push -u $remote $branch
} else {
git fetch $remote $branch
}
} else {
let s = (_git_status)
if $s.behind > 0 {
let r = if $rebase { [--rebase] } else { [] }
let a = if $autostash { [--autostash] } else { [] }
git pull $r $a -v
} else if $s.ahead > 0 {
git push
} else {
git fetch
}
}
}
# git add, rm and restore
export def ga [
file?: path
--all (-A): bool
--patch (-p): bool
--update (-u): bool
--verbose (-v): bool
--delete (-d): bool # git rm
--cached (-c): bool
--force (-f): bool
--restore (-r): bool # git restore
--staged (-s): bool
--source (-o): string
] {
if $delete {
let c = if $cached { [--cached] } else { [] }
let f = if $force { [--force] } else { [] }
git rm $c $file
} else if $restore {
let o = if ($source | is-empty) { [] } else { [--source $source] }
let s = if $staged { [--staged] } else { [] }
git restore $o $s $file
} else {
let a = if $all { [--all] } else { [] }
let p = if $patch { [--patch] } else { [] }
let u = if $update { [--update] } else { [] }
let v = if $verbose { [--verbose] } else { [] }
let file = if ($file | is-empty) { [.] } else { [$file] }
git add $a $p $u $v $file
}
}
# git commit
export def gc [
--message (-m): string
--all (-A): bool
--amend (-a): bool
--keep (-k): bool
] {
let m = if ($message | is-empty) { [] } else { [-m $message] }
let a = if $all { [--all] } else { [] }
let n = if $amend { [--amend] } else { [] }
let k = if $keep { [--no-edit] } else { [] }
git commit -v $m $a $n $k
}
# git diff
export def gd [
--cached (-c): bool # cached
--word-diff (-w): bool # word-diff
--staged (-s): bool # staged
] {
let w = if $word_diff { [--word-diff] } else { [] }
let c = if $cached { [--cached] } else { [] }
let s = if $staged { [--staged] } else { [] }
git diff $c $s $w
}
# git merge and rebase
export def gm [
branch?: string@"nu-complete git branches"
--rebase (-r): bool # git rebase
--onto (-o): string
--abort (-a): bool
--continue (-c): bool
--skip (-s): bool
--quit (-q): bool
] {
if $rebase {
if $abort {
git rebase --abort
} else if $continue {
git rebase --continue
} else if $skip {
git rebase --skip
} else if $quit {
git rebase --quit
} else if $onto {
git rebase --onto $branch
} else {
if ($branch | is-empty) {
git rebase (git_main_branch)
} else {
git rebase $branch
}
}
} else if ($branch | is-empty) {
git merge $"origin/(git_main_branch)"
} else {
git merge $branch
}
}
# git cherry-pick
export def gcp [
commit?: string@"nu-complete git log"
--abort (-a): bool
--continue (-c): bool
--skip (-s): bool
--quit (-q): bool
] {
if $abort {
git cherry-pick --abort
} else if $continue {
git cherry-pick --continue
} else if $skip {
git cherry-pick --skip
} else if $quit {
git cherry-pick --quit
} else {
git cherry-pick $commit
}
}
# git reset
export def gr [
commit?: string@"nu-complete git log"
--hard (-h): bool
] {
let h = if $hard { [--hard] } else { [] }
let c = if ($commit | is-empty) { [] } else { [$commit] }
git reset $h $c
}
# git remote
export def grmt [
remote?: string@"nu-complete git remotes"
uri?: string
--add (-a): bool
--rename (-r): bool
--delete (-d): bool
--update (-u): bool
--set (-s): bool
] {
if ($remote | is-empty) {
git remote -v
} else if $add {
git remote add $remote $uri
} else if $set {
git remote set-url $remote $uri
} else if $rename {
let old = $remote
let new = $uri
git remote rename $old $new
} else if $delete {
git remote remove $remote
} else if $update {
git remote update $remote
} else {
git remote show $remote
}
}
# git bisect
export def gbs [
--bad (-b): bool
--good (-g): bool
--reset (-r): bool
--start (-s): bool
] {
if $good {
git bisect good
} else if $bad {
git bisect bad
} else if $reset {
git bisect reset
} else if $start {
git bisect start
} else {
git bisect
}
}
export def gha [] {
git log --pretty=%h»¦«%aN»¦«%s»¦«%aD
| lines
| split column "»¦«" sha1 committer desc merged_at
| histogram committer merger
| sort-by merger
| reverse
}
export def gsq [] {
git reflog expire --all --expire=now
git gc --prune=now --aggressive
}
export def grb [branch:string@"nu-complete git branches"] {
git rebase (gstat).branch $branch
}
export alias gcf = git config --list
export alias gsw = git switch
export alias gswc = git switch -c
export alias gts = git tag -s
export def _git_status [] {
let raw_status = (do -i { git --no-optional-locks status --porcelain=2 --branch | lines })
mut status = {
idx_added_staged : 0
idx_modified_staged : 0
idx_deleted_staged : 0
idx_renamed : 0
idx_type_changed : 0
wt_untracked : 0
wt_modified : 0
wt_deleted : 0
wt_type_changed : 0
wt_renamed : 0
ignored : 0
conflicts : 0
ahead : 0
behind : 0
stashes : 0
repo_name : no_repository
tag : no_tag
branch : no_branch
remote : ''
}
if ($raw_status | is-empty) { return $status }
for s in $raw_status {
let r = ($s | split row ' ')
match $r.0 {
'#' => {
match ($r.1 | str substring 7..) {
'oid' => {
$status.commit_hash = ($r.2 | str substring 0..8)
}
'head' => {
$status.branch = $r.2
}
'upstream' => {
$status.remote = $r.2
}
'ab' => {
$status.ahead = ($r.2 | into int)
$status.behind = ($r.3 | into int | math abs)
}
}
}
'1'|'2' => {
match ($r.1 | str substring 0..1) {
'A' => {
$status.idx_added_staged += 1
}
'M' => {
$status.idx_modified_staged += 1
}
'R' => {
$status.idx_renamed += 1
}
'D' => {
$status.idx_deleted_staged += 1
}
'T' => {
$status.idx_type_changed += 1
}
}
match ($r.1 | str substring 1..2) {
'M' => {
$status.wt_modified += 1
}
'R' => {
$status.wt_renamed += 1
}
'D' => {
$status.wt_deleted += 1
}
'T' => {
$status.wt_type_changed += 1
}
}
}
'?' => {
$status.wt_untracked += 1
}
'u' => {
$status.conflicts += 1
}
}
}
$status
}
export def _git_log_stat [n] {
do -i {
git log -n $n --pretty=»¦«%h --stat
| lines
| reduce -f { c: '', r: [] } {|it, acc|
if ($it | str starts-with '»¦«') {
$acc | upsert c ($it | str substring 6.. )
} else if ($it | find -r '[0-9]+ file.+change' | is-empty) {
$acc
} else {
let x = (
$it
| split row ','
| each {|x| $x
| str trim
| parse -r "(?P<num>[0-9]+) (?P<col>.+)"
| get 0
}
| reduce -f {sha: $acc.c file:0 ins:0 del:0} {|i,a|
let col = if ($i.col | str starts-with 'file') {
'file'
} else {
$i.col | str substring ..3
}
let num = ($i.num | into int)
$a | upsert $col $num
}
)
$acc | upsert r ($acc.r | append $x)
}
}
| get r
}
}
export def _git_log [v num] {
let stat = if $v {
_git_log_stat $num
} else { {} }
let r = (do -i {
git log -n $num --pretty=%h»¦«%s»¦«%aN»¦«%aE»¦«%aD
| lines
| split column "»¦«" sha message author email date
| each {|x| ($x| upsert date ($x.date | into datetime))}
})
if $v {
$r | merge $stat | reverse
} else {
$r | reverse
}
}
def "nu-complete git log" [] {
git log -n 32 --pretty=%h»¦«%s
| lines
| split column "»¦«" value description
| each {|x| $x | update value $"($x.value)"}
}
export def "nu-complete git branches" [] {
git branch
| lines
| filter {|x| not ($x | str starts-with '*')}
| each {|x| $"($x|str trim)"}
}
def "nu-complete git remotes" [] {
^git remote | lines | each { |line| $line | str trim }
}
def git_main_branch [] {
git remote show origin
| lines
| str trim
| find --regex 'HEAD .*?[: ].+'
| first
| str replace 'HEAD .*?[: ](.+)' '$1'
}
def git_current_branch [] {
(gstat).branch
}

View file

@ -0,0 +1,3 @@
# Git branch cleanup
Remove any local git branch no longer being tracked in origin / remote repo.

View file

@ -198,21 +198,54 @@ def "nu-complete kube kind" [] {
def "nu-complete kube res" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let def = ($ctx | get args.1)
let kind = ($ctx | get args.1)
let ns = if ($ctx.-n? | is-empty) { [] } else { [-n $ctx.-n] }
kubectl get $ns $def | from ssv -a | get NAME
kubectl get $ns $kind | from ssv -a | get NAME
}
def "nu-complete kube res via name" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let cmd = ($ctx | get args.0)
let def = ($env.KUBERNETES_RESOURCE_ABBR | get ($cmd | str substring (($cmd | str length) - 1)..))
echo $'($cmd), ($def)' | save -a ~/.nulog
let kind = ($env.KUBERNETES_RESOURCE_ABBR | get ($ctx | get args.0 | str substring (-1..)))
let ns = if ($ctx.-n? | is-empty) { [] } else { [-n $ctx.-n] }
kubectl get $ns $def | from ssv -a | get NAME
kubectl get $ns $kind | from ssv -a | get NAME
}
def "nu-complete kube path" [context: string, offset: int] {
export def "nu-complete kube jsonpath" [context: string] {
let ctx = ($context | parse cmd)
let kind = ($ctx | get args.1)
let res = ($ctx | get args.2)
let path = $ctx.-p?
let ns = if ($ctx.-n? | is-empty) { [] } else { [-n $ctx.-n] }
mut r = []
if ($path | is-empty) {
if ($context | str ends-with '-p ') {
$r = ['.']
} else {
$r = ['']
}
} else if ($path | str starts-with '.') {
let row = ($path | split row '.')
let p = ($row | range ..-2 | str join '.')
if ($p | is-empty) {
$r = ( kubectl get $ns -o json $kind $res
| from json
| columns
| each {|x| $'($p).($x)'}
)
} else {
let m = (kubectl get $ns $kind $res $"--output=jsonpath={($p)}" | from json)
let l = ($row | last)
let c = (do -i {$m | get $l})
if (not ($c | is-empty)) and ($c | describe | str substring 0..5) == 'table' {
$r = (0..(($c | length) - 1) | each {|x| $'($p).($l)[($x)]'})
} else {
$r = ($m | columns | each {|x| $'($p).($x)'})
}
}
} else {
$r = ['']
}
$r
}
# kubectl get
@ -220,7 +253,7 @@ export def kg [
k: string@"nu-complete kube kind"
r?: string@"nu-complete kube res"
--namespace (-n): string@"nu-complete kube ns"
--jsonpath (-p): string@"nu-complete kube path"
--jsonpath (-p): string@"nu-complete kube jsonpath"
--selector (-l): string
--verbose (-v): bool
--watch (-w): bool
@ -255,7 +288,7 @@ export def kg [
kubectl get $n $k $r | from ssv -a
}
} else {
kubectl get $n $k $r $"--output=jsonpath={($jsonpath)}" | from yaml
kubectl get $n $k $r $"--output=jsonpath={($jsonpath)}" | from json
}
}
@ -339,7 +372,7 @@ def "nu-complete kube ctns" [context: string, offset: int] {
export def kgp [
r?: string@"nu-complete kube res via name"
--namespace (-n): string@"nu-complete kube ns"
--jsonpath (-p): string@"nu-complete kube path"
--jsonpath (-p): string@"nu-complete kube jsonpath"
--selector (-l): string
] {
kg pods -n $namespace -p $jsonpath -l $selector $r
@ -439,7 +472,7 @@ export def kcp [
export def kgs [
r?: string@"nu-complete kube res via name"
--namespace (-n): string@"nu-complete kube ns"
--jsonpath (-p): string@"nu-complete kube path"
--jsonpath (-p): string@"nu-complete kube jsonpath"
--selector (-l): string
] {
kg services -n $namespace -p $jsonpath -l $selector $r
@ -459,7 +492,7 @@ export def kdels [svc: string@"nu-complete kube res via name", -n: string@"nu-co
export def kgd [
r?: string@"nu-complete kube res via name"
--namespace (-n): string@"nu-complete kube ns"
--jsonpath (-p): string@"nu-complete kube path"
--jsonpath (-p): string@"nu-complete kube jsonpath"
--selector (-l): string
] {
kg -n $namespace deployments -p $jsonpath -l $selector $r

View file

@ -1,5 +1,5 @@
### git
export def git_status_raw [] {
export def git_status [] {
let raw_status = (do -i { git --no-optional-locks status --porcelain=2 --branch | lines })
mut status = {
@ -91,7 +91,7 @@ export def git_status_raw [] {
$status
}
export def git_status [] {
export def _git_status [] {
let status = (do -i { gstat })
if not ($status | is-empty) {
$status

View file

@ -1,23 +1,20 @@
### kubernetes
def ensure-cache-by-lines [cache path action] {
let ls = (do -i { open $path | lines | length })
if ($ls | is-empty) { return false }
let lc = (do -i { open $cache | get lines})
if not (($cache | path exists) and (not ($lc | is-empty)) and ($ls == $lc)) {
export def ensure-cache [cache path action] {
let ts = (do -i { ls $path | 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)
{
lines: $ls
payload: (do $action)
} | save -f $cache
do $action | save -f $cache
}
(open $cache).payload
open $cache
}
def "kube ctx" [] {
let cache = $'($env.HOME)/.cache/nu-power/kube.json'
let file = if ($env.KUBECONFIG? | is-empty) { $"($env.HOME)/.kube/config" } else { $env.KUBECONFIG }
if not ($file | path exists) { return $nothing }
ensure-cache-by-lines $cache $file {
ensure-cache $cache $file {
do -i {
kubectl config get-contexts
| from ssv -a