inital commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
#compdef biggest
|
||||
|
||||
# Completion for:
|
||||
# biggest [-n COUNT|--number COUNT] [-p PATTERN|--pattern PATTERN]
|
||||
|
||||
_arguments -s \
|
||||
'(-h --help)'{-h,--help}'[show help information]' \
|
||||
'(-n --number)'{-n,--number}'[number of entries to display]:number of entries:(10 20 50 100 200)' \
|
||||
'(-p --pattern)'{-p,--pattern}'[glob-style pattern to match]:glob pattern:_files' && return 0
|
||||
@@ -0,0 +1,212 @@
|
||||
#compdef colima
|
||||
compdef _colima colima
|
||||
|
||||
# zsh completion for colima -*- shell-script -*-
|
||||
|
||||
__colima_debug()
|
||||
{
|
||||
local file="$BASH_COMP_DEBUG_FILE"
|
||||
if [[ -n ${file} ]]; then
|
||||
echo "$*" >> "${file}"
|
||||
fi
|
||||
}
|
||||
|
||||
_colima()
|
||||
{
|
||||
local shellCompDirectiveError=1
|
||||
local shellCompDirectiveNoSpace=2
|
||||
local shellCompDirectiveNoFileComp=4
|
||||
local shellCompDirectiveFilterFileExt=8
|
||||
local shellCompDirectiveFilterDirs=16
|
||||
local shellCompDirectiveKeepOrder=32
|
||||
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
|
||||
local -a completions
|
||||
|
||||
__colima_debug "\n========= starting completion logic =========="
|
||||
__colima_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
|
||||
|
||||
# The user could have moved the cursor backwards on the command-line.
|
||||
# We need to trigger completion from the $CURRENT location, so we need
|
||||
# to truncate the command-line ($words) up to the $CURRENT location.
|
||||
# (We cannot use $CURSOR as its value does not work when a command is an alias.)
|
||||
words=("${=words[1,CURRENT]}")
|
||||
__colima_debug "Truncated words[*]: ${words[*]},"
|
||||
|
||||
lastParam=${words[-1]}
|
||||
lastChar=${lastParam[-1]}
|
||||
__colima_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
|
||||
|
||||
# For zsh, when completing a flag with an = (e.g., colima -n=<TAB>)
|
||||
# completions must be prefixed with the flag
|
||||
setopt local_options BASH_REMATCH
|
||||
if [[ "${lastParam}" =~ '-.*=' ]]; then
|
||||
# We are dealing with a flag with an =
|
||||
flagPrefix="-P ${BASH_REMATCH}"
|
||||
fi
|
||||
|
||||
# Prepare the command to obtain completions
|
||||
requestComp="${words[1]} __complete ${words[2,-1]}"
|
||||
if [ "${lastChar}" = "" ]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go completion code.
|
||||
__colima_debug "Adding extra empty parameter"
|
||||
requestComp="${requestComp} \"\""
|
||||
fi
|
||||
|
||||
__colima_debug "About to call: eval ${requestComp}"
|
||||
|
||||
# Use eval to handle any environment variables and such
|
||||
out=$(eval ${requestComp} 2>/dev/null)
|
||||
__colima_debug "completion output: ${out}"
|
||||
|
||||
# Extract the directive integer following a : from the last line
|
||||
local lastLine
|
||||
while IFS='\n' read -r line; do
|
||||
lastLine=${line}
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
__colima_debug "last line: ${lastLine}"
|
||||
|
||||
if [ "${lastLine[1]}" = : ]; then
|
||||
directive=${lastLine[2,-1]}
|
||||
# Remove the directive including the : and the newline
|
||||
local suffix
|
||||
(( suffix=${#lastLine}+2))
|
||||
out=${out[1,-$suffix]}
|
||||
else
|
||||
# There is no directive specified. Leave $out as is.
|
||||
__colima_debug "No directive found. Setting do default"
|
||||
directive=0
|
||||
fi
|
||||
|
||||
__colima_debug "directive: ${directive}"
|
||||
__colima_debug "completions: ${out}"
|
||||
__colima_debug "flagPrefix: ${flagPrefix}"
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
__colima_debug "Completion received error. Ignoring completions."
|
||||
return
|
||||
fi
|
||||
|
||||
local activeHelpMarker="_activeHelp_ "
|
||||
local endIndex=${#activeHelpMarker}
|
||||
local startIndex=$((${#activeHelpMarker}+1))
|
||||
local hasActiveHelp=0
|
||||
while IFS='\n' read -r comp; do
|
||||
# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
|
||||
if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
|
||||
__colima_debug "ActiveHelp found: $comp"
|
||||
comp="${comp[$startIndex,-1]}"
|
||||
if [ -n "$comp" ]; then
|
||||
compadd -x "${comp}"
|
||||
__colima_debug "ActiveHelp will need delimiter"
|
||||
hasActiveHelp=1
|
||||
fi
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$comp" ]; then
|
||||
# If requested, completions are returned with a description.
|
||||
# The description is preceded by a TAB character.
|
||||
# For zsh's _describe, we need to use a : instead of a TAB.
|
||||
# We first need to escape any : as part of the completion itself.
|
||||
comp=${comp//:/\\:}
|
||||
|
||||
local tab="$(printf '\t')"
|
||||
comp=${comp//$tab/:}
|
||||
|
||||
__colima_debug "Adding completion: ${comp}"
|
||||
completions+=${comp}
|
||||
lastComp=$comp
|
||||
fi
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
|
||||
# Add a delimiter after the activeHelp statements, but only if:
|
||||
# - there are completions following the activeHelp statements, or
|
||||
# - file completion will be performed (so there will be choices after the activeHelp)
|
||||
if [ $hasActiveHelp -eq 1 ]; then
|
||||
if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
|
||||
__colima_debug "Adding activeHelp delimiter"
|
||||
compadd -x "--"
|
||||
hasActiveHelp=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
__colima_debug "Activating nospace."
|
||||
noSpace="-S ''"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
__colima_debug "Activating keep order."
|
||||
keepOrder="-V"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# File extension filtering
|
||||
local filteringCmd
|
||||
filteringCmd='_files'
|
||||
for filter in ${completions[@]}; do
|
||||
if [ ${filter[1]} != '*' ]; then
|
||||
# zsh requires a glob pattern to do file filtering
|
||||
filter="\*.$filter"
|
||||
fi
|
||||
filteringCmd+=" -g $filter"
|
||||
done
|
||||
filteringCmd+=" ${flagPrefix}"
|
||||
|
||||
__colima_debug "File filtering command: $filteringCmd"
|
||||
_arguments '*:filename:'"$filteringCmd"
|
||||
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
# File completion for directories only
|
||||
local subdir
|
||||
subdir="${completions[1]}"
|
||||
if [ -n "$subdir" ]; then
|
||||
__colima_debug "Listing directories in $subdir"
|
||||
pushd "${subdir}" >/dev/null 2>&1
|
||||
else
|
||||
__colima_debug "Listing directories in ."
|
||||
fi
|
||||
|
||||
local result
|
||||
_arguments '*:dirname:_files -/'" ${flagPrefix}"
|
||||
result=$?
|
||||
if [ -n "$subdir" ]; then
|
||||
popd >/dev/null 2>&1
|
||||
fi
|
||||
return $result
|
||||
else
|
||||
__colima_debug "Calling _describe"
|
||||
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
|
||||
__colima_debug "_describe found some completions"
|
||||
|
||||
# Return the success of having called _describe
|
||||
return 0
|
||||
else
|
||||
__colima_debug "_describe did not find completions."
|
||||
__colima_debug "Checking if we should do file completion."
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
__colima_debug "deactivating file completion"
|
||||
|
||||
# We must return an error code here to let zsh know that there were no
|
||||
# completions found by _describe; this is what will trigger other
|
||||
# matching algorithms to attempt to find completions.
|
||||
# For example zsh can match letters in the middle of words.
|
||||
return 1
|
||||
else
|
||||
# Perform file completion
|
||||
__colima_debug "Activating file completion"
|
||||
|
||||
# We must return the result of this command, so it must be the
|
||||
# last command, or else we must store its result to return it.
|
||||
_arguments '*:filename:_files'" ${flagPrefix}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# don't run the completion function when being source-ed or eval-ed
|
||||
if [ "$funcstack[1]" = "_colima" ]; then
|
||||
_colima
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
#compdef compinit_doctor
|
||||
|
||||
local -a args
|
||||
args=(
|
||||
'(-h --help)'{-h,--help}'[Show help/usage]'
|
||||
'(-s --status)'{-s,--status}'[Show current compinit/dump status (default)]'
|
||||
'(-a --audit)'{-a,--audit}'[Run compaudit for insecure paths]'
|
||||
'(-r --rebuild)'{-r,--rebuild}'[Force compinit -i -d and background zcompile]'
|
||||
'(-S --snap)'{-S,--snap}'[Snapshot current $fpath to file]:snapshot file:_files'
|
||||
'(-d --diff)'{-d,--diff}'[Diff current $fpath against a snapshot file]:snapshot file:_files'
|
||||
'(-D --dump)'{-D,--dump}'[Override zcompdump path]:dump file:_files -g "*zcompdump(-.)"'
|
||||
'(-j --json)'{-j,--json}'[Emit status as single JSON object]'
|
||||
)
|
||||
|
||||
_arguments -s -S $args '*:file:_files'
|
||||
@@ -0,0 +1,33 @@
|
||||
#compdef confe
|
||||
|
||||
# Completion for the confe function
|
||||
|
||||
local -a topics opts
|
||||
|
||||
topics=(
|
||||
alias
|
||||
keybindings
|
||||
options
|
||||
path
|
||||
packages
|
||||
ssh
|
||||
brewfile
|
||||
)
|
||||
|
||||
opts=(
|
||||
'--backup[Backup the file before editing]'
|
||||
'-b[Backup the file before editing]'
|
||||
'--staged[Stage the file into its git repo after editing]'
|
||||
'-s[Stage the file into its git repo after editing]'
|
||||
)
|
||||
|
||||
_arguments \
|
||||
$opts \
|
||||
'1:topic:->topic' \
|
||||
'*:filename:_files' && return 0
|
||||
|
||||
case $state in
|
||||
topic)
|
||||
_describe 'topic' topics
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,89 @@
|
||||
#compdef copy_files
|
||||
|
||||
_copy_files() {
|
||||
local -a dirs
|
||||
local -a cp_hosts_default
|
||||
local -a used_hosts avail_hosts
|
||||
local _dotfiles="${DOTFILES:-$HOME/.dotfiles}"
|
||||
local i host_mode=0 host_arg_index=0
|
||||
|
||||
cp_hosts_default=( $(_copy_files_ssh_hosts) )
|
||||
|
||||
[[ -d "$_dotfiles" ]] || return 1
|
||||
dirs=(${(f)"$(print -rl -- $_dotfiles/*(/N:t))"})
|
||||
|
||||
for (( i = 2; i < CURRENT; i++ )); do
|
||||
case "${words[i]}" in
|
||||
-h|--hosts)
|
||||
host_mode=1
|
||||
host_arg_index=$i
|
||||
;;
|
||||
--)
|
||||
(( host_mode )) && host_mode=0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( host_mode )); then
|
||||
used_hosts=()
|
||||
|
||||
for (( i = host_arg_index + 1; i < CURRENT; i++ )); do
|
||||
[[ "${words[i]}" == "--" ]] && break
|
||||
used_hosts+=("${words[i]}")
|
||||
done
|
||||
|
||||
avail_hosts=( "${(@)cp_hosts_default:|used_hosts}" )
|
||||
compadd -S '' -- "${avail_hosts[@]}" --
|
||||
return
|
||||
fi
|
||||
|
||||
_describe -t dotfiles 'dotfiles subdirectory' dirs
|
||||
compadd -S '' -- -h --hosts
|
||||
}
|
||||
|
||||
_copy_files_ssh_hosts() {
|
||||
local cfg="${HOME}/.ssh/config"
|
||||
[[ -r "$cfg" ]] || return 0
|
||||
|
||||
awk '
|
||||
BEGIN {
|
||||
in_block = 0
|
||||
has_hostname = 0
|
||||
aliases = ""
|
||||
}
|
||||
|
||||
/^[[:space:]]*#/ { next }
|
||||
/^[[:space:]]*$/ { next }
|
||||
|
||||
tolower($1) == "host" {
|
||||
if (in_block && has_hostname && aliases != "") {
|
||||
print aliases
|
||||
}
|
||||
|
||||
in_block = 1
|
||||
has_hostname = 0
|
||||
aliases = ""
|
||||
|
||||
for (i = 2; i <= NF; i++) {
|
||||
if ($i ~ /[*?\[]/) {
|
||||
continue
|
||||
}
|
||||
aliases = aliases $i "\n"
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
in_block && tolower($1) == "hostname" {
|
||||
has_hostname = 1
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
if (in_block && has_hostname && aliases != "") {
|
||||
print aliases
|
||||
}
|
||||
}
|
||||
' "$cfg" | awk '!seen[$0]++'
|
||||
}
|
||||
|
||||
_copy_files "$@"
|
||||
@@ -0,0 +1,11 @@
|
||||
#compdef dcs
|
||||
|
||||
# dcs is a shim to dcsctl, so reuse the same completion.
|
||||
# This ensures `dcs …` and `dcsctl …` behave identically.
|
||||
|
||||
_dcs() {
|
||||
autoload -Uz _dcsctl
|
||||
_dcsctl "$@"
|
||||
}
|
||||
|
||||
_dcs "$@"
|
||||
@@ -0,0 +1,445 @@
|
||||
#compdef dcsctl
|
||||
|
||||
# --- helpers ---------------------------------------------------------------
|
||||
|
||||
_dcsctl__docker_contexts() {
|
||||
local -a ctxs
|
||||
ctxs=(${(f)"$(command docker context ls --format '{{.Name}}' 2>/dev/null)"})
|
||||
_describe -t docker-contexts 'docker contexts' ctxs
|
||||
}
|
||||
|
||||
_dcsctl__current_context() {
|
||||
command docker context show 2>/dev/null
|
||||
}
|
||||
|
||||
_dcsctl__control_plane_base() {
|
||||
# Match dcsctl logic: base is $DCS_ROOT if set, else ~/.dcs
|
||||
print -r -- "${DCS_ROOT:-$HOME/.dcs}"
|
||||
}
|
||||
|
||||
_dcsctl__projects() {
|
||||
local ctx base
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 0
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
[[ -d "$base" ]] || return 0
|
||||
|
||||
local -a ignored projects
|
||||
ignored=(shared '@Recycle' '@Recently-Snapshot' '.DS_Store')
|
||||
|
||||
projects=()
|
||||
local dir name
|
||||
for dir in "$base"/*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
(( ${ignored[(I)$name]} )) && continue
|
||||
projects+=("$name")
|
||||
done
|
||||
|
||||
_describe -t dcs-projects 'projects' projects
|
||||
}
|
||||
|
||||
_dcsctl__secret_names_from_dir() {
|
||||
local ctx proj base projdir secretdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[4]}" # dcsctl secret add <project> <name>
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
secretdir="$projdir/secrets"
|
||||
[[ -d "$secretdir" ]] || return 1
|
||||
|
||||
local -a names
|
||||
names=()
|
||||
local f n
|
||||
for f in "$secretdir"/*.txt; do
|
||||
[[ -f "$f" ]] || continue
|
||||
n="${f:t}"
|
||||
n="${n%.txt}"
|
||||
names+=("$n")
|
||||
done
|
||||
|
||||
(( ${#names} == 0 )) && return 1
|
||||
_describe -t dcs-secrets 'secret names' names
|
||||
}
|
||||
|
||||
# List service subdirectories in a swarm-mode project.
|
||||
# Takes one argument: the project name.
|
||||
_dcsctl__swarm_services() {
|
||||
local ctx proj base projdir svcdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="$1"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
svcdir="$projdir/services"
|
||||
[[ -d "$svcdir" ]] || return 1
|
||||
|
||||
local -a svcs
|
||||
svcs=()
|
||||
local dir name
|
||||
for dir in "$svcdir"/*/; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
svcs+=("$name")
|
||||
done
|
||||
|
||||
(( ${#svcs} == 0 )) && return 1
|
||||
_describe -t dcs-swarm-services 'services' svcs
|
||||
}
|
||||
|
||||
# Detect whether a project is in swarm mode (has services/ dir).
|
||||
_dcsctl__is_swarm() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="$1"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
[[ -d "$projdir/services" ]]
|
||||
}
|
||||
|
||||
# Fetch services for a given project by asking docker compose for the resolved config.
|
||||
_dcsctl__services() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[3]}" # dcsctl run <project> ...
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
[[ -d "$projdir" ]] || return 1
|
||||
|
||||
local env1 env2 env3
|
||||
env1="$projdir/.env"
|
||||
env2="$projdir/service.env"
|
||||
env3="$projdir/service.secrets.env"
|
||||
|
||||
[[ -f "$env1" && -f "$env2" && -f "$env3" && -f "$projdir/docker-compose.yml" ]] || return 1
|
||||
|
||||
local -a svcs
|
||||
svcs=(${(f)"$(
|
||||
command docker --context "$ctx" compose \
|
||||
--project-directory "$projdir" \
|
||||
--project-name "$proj" \
|
||||
--env-file "$env1" \
|
||||
--env-file "$env2" \
|
||||
--env-file "$env3" \
|
||||
config --services 2>/dev/null
|
||||
)"})
|
||||
|
||||
(( ${#svcs} == 0 )) && return 1
|
||||
_describe -t dcs-services 'services' svcs
|
||||
}
|
||||
|
||||
# Heuristic: decide if we are in a position where a service name is expected.
|
||||
_dcsctl__maybe_complete_service() {
|
||||
local subcmd cur
|
||||
subcmd="${words[4]}" # dcsctl run <project> <subcmd> ...
|
||||
cur="${words[CURRENT]}"
|
||||
|
||||
[[ "$cur" == -* ]] && return 1
|
||||
|
||||
case "$subcmd" in
|
||||
logs|ps|stop|start|restart|up|pull|build|rm|kill|pause|unpause|top|exec)
|
||||
_dcsctl__services
|
||||
return $?
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Delegate completion to docker after we reconstruct words as docker compose ...
|
||||
_dcsctl__docker_compose_delegate() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[3]}"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
|
||||
local -a dc_words
|
||||
dc_words=(docker --context "$ctx" compose --project-directory "$projdir" --project-name "$proj")
|
||||
|
||||
if (( ${#words} >= 4 )); then
|
||||
dc_words+=("${words[@]:3}") # from 4th element
|
||||
fi
|
||||
|
||||
local -a save_words
|
||||
local save_cword
|
||||
save_words=("${words[@]}")
|
||||
save_cword=$CURRENT
|
||||
|
||||
words=("${dc_words[@]}")
|
||||
CURRENT=${#words}
|
||||
|
||||
autoload -Uz _docker
|
||||
_docker
|
||||
|
||||
words=("${save_words[@]}")
|
||||
CURRENT=$save_cword
|
||||
}
|
||||
|
||||
# Helper for edit --service completion: extract the project arg from the command
|
||||
# line and complete swarm service names.
|
||||
_dcsctl__swarm_services_for_edit() {
|
||||
local proj=""
|
||||
local i
|
||||
for (( i=3; i <= ${#words}; i++ )); do
|
||||
local w="${words[$i]}"
|
||||
case "$w" in
|
||||
-e|--env|-r|--runtime|-s|--secret) ;;
|
||||
--service) (( i++ )) ;; # skip the next arg (the service value)
|
||||
-*) ;;
|
||||
*)
|
||||
if [[ -z "$proj" ]]; then
|
||||
proj="$w"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$proj" ]] && return 1
|
||||
_dcsctl__swarm_services "$proj"
|
||||
}
|
||||
|
||||
# --- main completion -------------------------------------------------------
|
||||
|
||||
_dcsctl() {
|
||||
local -a subcmds
|
||||
subcmds=(
|
||||
'new:Create a new project scaffold'
|
||||
'verify:Verify and enforce host permissions for appdata bind mounts'
|
||||
'run:Run docker compose for a project (compose mode)'
|
||||
'up:Alias for run <project> up -d (compose mode)'
|
||||
'down:Bring down a project (compose or swarm)'
|
||||
'stop:Scale all services to 0 replicas (swarm projects only)'
|
||||
'deploy:Merge service fragments and deploy as Docker Swarm stack'
|
||||
'ls:Alias for docker compose ls'
|
||||
'dir:Print the project directory, or a service directory in a swarm project'
|
||||
'edit:Edit project files in $EDITOR'
|
||||
'secret:Manage project secrets'
|
||||
'service:Manage services within a swarm project'
|
||||
'import:Import an existing docker-compose.yml into a DCS project'
|
||||
'context:Manage DCS control-plane contexts'
|
||||
'reload:Down, rebuild and up all containers within a DCS project'
|
||||
'rename:Rename and update a DCS project'
|
||||
'help:Show help for a command'
|
||||
)
|
||||
|
||||
if (( CURRENT == 2 )); then
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
return
|
||||
fi
|
||||
|
||||
local cmd
|
||||
cmd="${words[2]}"
|
||||
|
||||
case "$cmd" in
|
||||
new)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run verification after creating the project]' \
|
||||
'--verbose[Verbose output for verification (used with --verify)]' \
|
||||
'--compose[Create flat single-compose layout (legacy)]' \
|
||||
'--service[Initial service name for swarm layout]:service name:' \
|
||||
'1:project name:' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
verify)
|
||||
_arguments -C \
|
||||
'--verbose[Show current vs expected ownership/mode while verifying (QNAP only)]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
run)
|
||||
if (( CURRENT == 3 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
|
||||
if (( CURRENT >= 5 )); then
|
||||
_dcsctl__maybe_complete_service && return
|
||||
fi
|
||||
|
||||
_dcsctl__docker_compose_delegate
|
||||
return
|
||||
;;
|
||||
|
||||
up)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run `dcsctl verify` before running docker compose]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
down)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
stop)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
deploy)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run verification before deploying]' \
|
||||
'--verbose[Verbose verification output]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
ls)
|
||||
return
|
||||
;;
|
||||
|
||||
dir)
|
||||
if (( CURRENT == 3 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
if (( CURRENT == 4 )); then
|
||||
local proj="${words[3]}"
|
||||
_dcsctl__swarm_services "$proj" && return
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
edit)
|
||||
_arguments -C \
|
||||
'(-e --env -r --runtime -s --secret)'{-e,--env}'[Edit .env]' \
|
||||
'(-e --env -r --runtime -s --secret)'{-r,--runtime}'[Edit service.env]' \
|
||||
'(-e --env -r --runtime -s --secret)'{-s,--secret}'[Edit service.secrets.env]' \
|
||||
'--service[Target a specific service (swarm mode)]:service name:_dcsctl__swarm_services_for_edit' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
secret)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t secret-subcmds 'secret commands' \
|
||||
'add:Create/open secrets/<name>.txt in $EDITOR'
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
add)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
if (( CURRENT == 5 )); then
|
||||
_dcsctl__secret_names_from_dir && return
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
service)
|
||||
if (( CURRENT == 3 )); then
|
||||
local -a service_subcmds
|
||||
service_subcmds=(
|
||||
'add:Add a new service to a swarm project'
|
||||
'ls:List services in a swarm project'
|
||||
)
|
||||
_describe -t service-subcmds 'service commands' service_subcmds
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
add)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
# CURRENT == 5: free-form service name, no completions needed
|
||||
;;
|
||||
ls)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
import)
|
||||
_arguments -C \
|
||||
'--env-file[Additional env file to ingest]:env file:_files' \
|
||||
'--strict[Fail if compose violates template contract after import]' \
|
||||
'--swarm[Import as swarm project, splitting services into per-service fragments]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
'2:compose path:_files' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
context)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t context-subcmds 'context commands' \
|
||||
'init:Initialize ~/.dcs/<context> with env.system and compose/'
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
init)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__docker_contexts
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
reload)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
rename)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
'2:new project name:' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
help)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
}
|
||||
|
||||
_dcsctl "$@"
|
||||
@@ -0,0 +1,12 @@
|
||||
#compdef dot
|
||||
|
||||
_dot() {
|
||||
local -a dirs
|
||||
local _dotfiles="${DOTFILES:-$HOME/.dotfiles}"
|
||||
|
||||
[[ -d "$_dotfiles" ]] || return 1
|
||||
|
||||
dirs=(${(f)"$(print -rl -- $_dotfiles/*(/N:t))"})
|
||||
|
||||
_describe 'dotfiles subdirectory' dirs
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#compdef dotfiles_doctor
|
||||
|
||||
# Zsh completion for dotfiles_doctor
|
||||
# Supports: --timeline, --logfile=PATH (completes *.json)
|
||||
# Usage: place this file on your $fpath, then compinit (or let your compinit.zsh rebuild)
|
||||
|
||||
local -a _args
|
||||
_args=(
|
||||
'--timeline[Show per-file start/end timeline with μs precision]'
|
||||
'--logfile=[Path to NDJSON log (default: ~/zshrc-log.json)]:logfile:_files -g "*.json"'
|
||||
'--help[Show usage]'
|
||||
)
|
||||
|
||||
_arguments -s -S $_args \
|
||||
'*:filename:_files'
|
||||
@@ -0,0 +1,7 @@
|
||||
#compdef find_dupes
|
||||
|
||||
_arguments \
|
||||
'(-d --maxdepth)'{-d,--maxdepth}'[maximum recursion depth]:maxdepth (int):_guard "[0-9]#" "integer depth"' \
|
||||
'(-r --regex)'{-r,--regex}'[POSIX extended regular expression]:regex (ERE)' \
|
||||
'(-i --ignore-case)'{-i,--ignore-case}'[match regex case-insensitively]' \
|
||||
'*: :->rest'
|
||||
@@ -0,0 +1,228 @@
|
||||
#compdef kubectl
|
||||
compdef _kubectl kubectl
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#compdef kubectl
|
||||
compdef _kubectl kubectl
|
||||
|
||||
# zsh completion for kubectl -*- shell-script -*-
|
||||
|
||||
__kubectl_debug()
|
||||
{
|
||||
local file="$BASH_COMP_DEBUG_FILE"
|
||||
if [[ -n ${file} ]]; then
|
||||
echo "$*" >> "${file}"
|
||||
fi
|
||||
}
|
||||
|
||||
_kubectl()
|
||||
{
|
||||
local shellCompDirectiveError=1
|
||||
local shellCompDirectiveNoSpace=2
|
||||
local shellCompDirectiveNoFileComp=4
|
||||
local shellCompDirectiveFilterFileExt=8
|
||||
local shellCompDirectiveFilterDirs=16
|
||||
local shellCompDirectiveKeepOrder=32
|
||||
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
|
||||
local -a completions
|
||||
|
||||
__kubectl_debug "\n========= starting completion logic =========="
|
||||
__kubectl_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
|
||||
|
||||
# The user could have moved the cursor backwards on the command-line.
|
||||
# We need to trigger completion from the $CURRENT location, so we need
|
||||
# to truncate the command-line ($words) up to the $CURRENT location.
|
||||
# (We cannot use $CURSOR as its value does not work when a command is an alias.)
|
||||
words=("${=words[1,CURRENT]}")
|
||||
__kubectl_debug "Truncated words[*]: ${words[*]},"
|
||||
|
||||
lastParam=${words[-1]}
|
||||
lastChar=${lastParam[-1]}
|
||||
__kubectl_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
|
||||
|
||||
# For zsh, when completing a flag with an = (e.g., kubectl -n=<TAB>)
|
||||
# completions must be prefixed with the flag
|
||||
setopt local_options BASH_REMATCH
|
||||
if [[ "${lastParam}" =~ '-.*=' ]]; then
|
||||
# We are dealing with a flag with an =
|
||||
flagPrefix="-P ${BASH_REMATCH}"
|
||||
fi
|
||||
|
||||
# Prepare the command to obtain completions
|
||||
requestComp="${words[1]} __complete ${words[2,-1]}"
|
||||
if [ "${lastChar}" = "" ]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go completion code.
|
||||
__kubectl_debug "Adding extra empty parameter"
|
||||
requestComp="${requestComp} \"\""
|
||||
fi
|
||||
|
||||
__kubectl_debug "About to call: eval ${requestComp}"
|
||||
|
||||
# Use eval to handle any environment variables and such
|
||||
out=$(eval ${requestComp} 2>/dev/null)
|
||||
__kubectl_debug "completion output: ${out}"
|
||||
|
||||
# Extract the directive integer following a : from the last line
|
||||
local lastLine
|
||||
while IFS='\n' read -r line; do
|
||||
lastLine=${line}
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
__kubectl_debug "last line: ${lastLine}"
|
||||
|
||||
if [ "${lastLine[1]}" = : ]; then
|
||||
directive=${lastLine[2,-1]}
|
||||
# Remove the directive including the : and the newline
|
||||
local suffix
|
||||
(( suffix=${#lastLine}+2))
|
||||
out=${out[1,-$suffix]}
|
||||
else
|
||||
# There is no directive specified. Leave $out as is.
|
||||
__kubectl_debug "No directive found. Setting do default"
|
||||
directive=0
|
||||
fi
|
||||
|
||||
__kubectl_debug "directive: ${directive}"
|
||||
__kubectl_debug "completions: ${out}"
|
||||
__kubectl_debug "flagPrefix: ${flagPrefix}"
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
__kubectl_debug "Completion received error. Ignoring completions."
|
||||
return
|
||||
fi
|
||||
|
||||
local activeHelpMarker="_activeHelp_ "
|
||||
local endIndex=${#activeHelpMarker}
|
||||
local startIndex=$((${#activeHelpMarker}+1))
|
||||
local hasActiveHelp=0
|
||||
while IFS='\n' read -r comp; do
|
||||
# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
|
||||
if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
|
||||
__kubectl_debug "ActiveHelp found: $comp"
|
||||
comp="${comp[$startIndex,-1]}"
|
||||
if [ -n "$comp" ]; then
|
||||
compadd -x "${comp}"
|
||||
__kubectl_debug "ActiveHelp will need delimiter"
|
||||
hasActiveHelp=1
|
||||
fi
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$comp" ]; then
|
||||
# If requested, completions are returned with a description.
|
||||
# The description is preceded by a TAB character.
|
||||
# For zsh's _describe, we need to use a : instead of a TAB.
|
||||
# We first need to escape any : as part of the completion itself.
|
||||
comp=${comp//:/\\:}
|
||||
|
||||
local tab="$(printf '\t')"
|
||||
comp=${comp//$tab/:}
|
||||
|
||||
__kubectl_debug "Adding completion: ${comp}"
|
||||
completions+=${comp}
|
||||
lastComp=$comp
|
||||
fi
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
|
||||
# Add a delimiter after the activeHelp statements, but only if:
|
||||
# - there are completions following the activeHelp statements, or
|
||||
# - file completion will be performed (so there will be choices after the activeHelp)
|
||||
if [ $hasActiveHelp -eq 1 ]; then
|
||||
if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
|
||||
__kubectl_debug "Adding activeHelp delimiter"
|
||||
compadd -x "--"
|
||||
hasActiveHelp=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
__kubectl_debug "Activating nospace."
|
||||
noSpace="-S ''"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
__kubectl_debug "Activating keep order."
|
||||
keepOrder="-V"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# File extension filtering
|
||||
local filteringCmd
|
||||
filteringCmd='_files'
|
||||
for filter in ${completions[@]}; do
|
||||
if [ ${filter[1]} != '*' ]; then
|
||||
# zsh requires a glob pattern to do file filtering
|
||||
filter="\*.$filter"
|
||||
fi
|
||||
filteringCmd+=" -g $filter"
|
||||
done
|
||||
filteringCmd+=" ${flagPrefix}"
|
||||
|
||||
__kubectl_debug "File filtering command: $filteringCmd"
|
||||
_arguments '*:filename:'"$filteringCmd"
|
||||
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
# File completion for directories only
|
||||
local subdir
|
||||
subdir="${completions[1]}"
|
||||
if [ -n "$subdir" ]; then
|
||||
__kubectl_debug "Listing directories in $subdir"
|
||||
pushd "${subdir}" >/dev/null 2>&1
|
||||
else
|
||||
__kubectl_debug "Listing directories in ."
|
||||
fi
|
||||
|
||||
local result
|
||||
_arguments '*:dirname:_files -/'" ${flagPrefix}"
|
||||
result=$?
|
||||
if [ -n "$subdir" ]; then
|
||||
popd >/dev/null 2>&1
|
||||
fi
|
||||
return $result
|
||||
else
|
||||
__kubectl_debug "Calling _describe"
|
||||
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
|
||||
__kubectl_debug "_describe found some completions"
|
||||
|
||||
# Return the success of having called _describe
|
||||
return 0
|
||||
else
|
||||
__kubectl_debug "_describe did not find completions."
|
||||
__kubectl_debug "Checking if we should do file completion."
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
__kubectl_debug "deactivating file completion"
|
||||
|
||||
# We must return an error code here to let zsh know that there were no
|
||||
# completions found by _describe; this is what will trigger other
|
||||
# matching algorithms to attempt to find completions.
|
||||
# For example zsh can match letters in the middle of words.
|
||||
return 1
|
||||
else
|
||||
# Perform file completion
|
||||
__kubectl_debug "Activating file completion"
|
||||
|
||||
# We must return the result of this command, so it must be the
|
||||
# last command, or else we must store its result to return it.
|
||||
_arguments '*:filename:_files'" ${flagPrefix}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# don't run the completion function when being source-ed or eval-ed
|
||||
if [ "$funcstack[1]" = "_kubectl" ]; then
|
||||
_kubectl
|
||||
fi
|
||||
@@ -0,0 +1,28 @@
|
||||
#compdef pdir
|
||||
|
||||
_pdir_projects() {
|
||||
local ctx base
|
||||
ctx="$(docker context show 2>/dev/null)"
|
||||
[[ -z "$ctx" ]] && return 0
|
||||
|
||||
# Respect DOCKER_DIR override (same as your dcsctl behavior)
|
||||
base="${DOCKER_DIR:-$HOME/.dcs}"
|
||||
base="$base/$ctx/compose"
|
||||
[[ -d "$base" ]] || return 0
|
||||
|
||||
local -a ignored projects
|
||||
ignored=(shared '@Recycle' '@Recently-Snapshot' '.DS_Store')
|
||||
|
||||
projects=()
|
||||
local dir name
|
||||
for dir in "$base"/*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
(( ${ignored[(I)$name]} )) && continue
|
||||
projects+=("$name")
|
||||
done
|
||||
|
||||
_describe -t dcs-projects 'projects' projects
|
||||
}
|
||||
|
||||
_arguments -C '1:project:_pdir_projects'
|
||||
Reference in New Issue
Block a user