Current section
Files
Jump to
Current section
Files
lib/secrethub_cli/completion.ex
defmodule SecretHub.CLI.Completion do
@moduledoc """
Shell completion generator for SecretHub CLI.
Generates completion scripts for Bash and Zsh shells that provide:
- Command and subcommand completion
- Option completion with descriptions
- Dynamic completion for secret paths, policies, and agents
- Context-aware suggestions
## Usage
# Generate Bash completion
SecretHub.CLI.Completion.generate(:bash)
# Generate Zsh completion
SecretHub.CLI.Completion.generate(:zsh)
"""
@bash_completion """
# Bash completion for SecretHub CLI
# Generated by SecretHub.CLI.Completion
_secrethub() {
local cur prev opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Global options
local global_opts="--help --version --server --format --quiet --verbose -h -v -s -f -q"
local format_opts="json table yaml"
# Main commands
local commands="login logout whoami secret policy agent config completion help version"
# Subcommands
local secret_commands="list get create update delete versions rollback"
local policy_commands="list get create update delete simulate templates"
local agent_commands="list status logs"
local config_commands="list get set"
# Handle format option completion
if [[ "${prev}" == "--format" ]] || [[ "${prev}" == "-f" ]]; then
COMPREPLY=( $(compgen -W "${format_opts}" -- ${cur}) )
return 0
fi
# Handle server option completion
if [[ "${prev}" == "--server" ]] || [[ "${prev}" == "-s" ]]; then
# No completion for server URL
return 0
fi
# Handle login options
if [[ "${prev}" == "--role-id" ]] || [[ "${prev}" == "--secret-id" ]] || \
[[ "${prev}" == "--value" ]] || [[ "${prev}" == "--name" ]]; then
# No completion for value inputs
return 0
fi
# Handle from-template option
if [[ "${prev}" == "--from-template" ]]; then
local templates="$(_secrethub_get_policy_templates)"
COMPREPLY=( $(compgen -W "${templates}" -- ${cur}) )
return 0
fi
# Determine context based on command words
local cmd="${COMP_WORDS[1]}"
local subcmd="${COMP_WORDS[2]}"
# Completion for completion command
if [[ "${cmd}" == "completion" ]]; then
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=( $(compgen -W "bash zsh" -- ${cur}) )
fi
return 0
fi
# Completion for secret commands
if [[ "${cmd}" == "secret" ]]; then
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=( $(compgen -W "${secret_commands}" -- ${cur}) )
return 0
elif [[ ${COMP_CWORD} -eq 3 ]]; then
case "${subcmd}" in
get|update|delete|versions|rollback)
local secrets="$(_secrethub_get_secrets)"
COMPREPLY=( $(compgen -W "${secrets}" -- ${cur}) )
return 0
;;
create)
# No completion for new secret path
return 0
;;
list)
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
;;
esac
elif [[ ${COMP_CWORD} -gt 3 ]]; then
# Complete options for secret commands
local secret_opts="--value --from-file ${global_opts}"
COMPREPLY=( $(compgen -W "${secret_opts}" -- ${cur}) )
return 0
fi
fi
# Completion for policy commands
if [[ "${cmd}" == "policy" ]]; then
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=( $(compgen -W "${policy_commands}" -- ${cur}) )
return 0
elif [[ ${COMP_CWORD} -eq 3 ]]; then
case "${subcmd}" in
get|update|delete|simulate)
local policies="$(_secrethub_get_policies)"
COMPREPLY=( $(compgen -W "${policies}" -- ${cur}) )
return 0
;;
create)
local policy_opts="--from-template --name ${global_opts}"
COMPREPLY=( $(compgen -W "${policy_opts}" -- ${cur}) )
return 0
;;
list|templates)
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
;;
esac
elif [[ ${COMP_CWORD} -gt 3 ]]; then
# Complete options for policy commands
local policy_opts="--from-template --name ${global_opts}"
COMPREPLY=( $(compgen -W "${policy_opts}" -- ${cur}) )
return 0
fi
fi
# Completion for agent commands
if [[ "${cmd}" == "agent" ]]; then
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=( $(compgen -W "${agent_commands}" -- ${cur}) )
return 0
elif [[ ${COMP_CWORD} -eq 3 ]]; then
case "${subcmd}" in
status|logs)
local agents="$(_secrethub_get_agents)"
COMPREPLY=( $(compgen -W "${agents}" -- ${cur}) )
return 0
;;
list)
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
;;
esac
elif [[ ${COMP_CWORD} -gt 3 ]]; then
# Complete options for agent commands
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
fi
fi
# Completion for config commands
if [[ "${cmd}" == "config" ]]; then
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=( $(compgen -W "${config_commands}" -- ${cur}) )
return 0
elif [[ ${COMP_CWORD} -eq 3 ]]; then
case "${subcmd}" in
get|set)
local config_keys="server_url default_format log_level"
COMPREPLY=( $(compgen -W "${config_keys}" -- ${cur}) )
return 0
;;
list)
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
;;
esac
elif [[ ${COMP_CWORD} -eq 4 ]]; then
case "${subcmd}" in
set)
# Complete value for config set
local key="${COMP_WORDS[3]}"
if [[ "${key}" == "default_format" ]]; then
COMPREPLY=( $(compgen -W "${format_opts}" -- ${cur}) )
fi
return 0
;;
esac
fi
fi
# Completion for login command
if [[ "${cmd}" == "login" ]]; then
local login_opts="--role-id --secret-id ${global_opts}"
COMPREPLY=( $(compgen -W "${login_opts}" -- ${cur}) )
return 0
fi
# Completion for logout and whoami (only global options)
if [[ "${cmd}" == "logout" ]] || [[ "${cmd}" == "whoami" ]]; then
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
fi
# Main command completion
if [[ ${COMP_CWORD} -eq 1 ]]; then
COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
return 0
fi
# Default to global options
COMPREPLY=( $(compgen -W "${global_opts}" -- ${cur}) )
return 0
}
# Helper function to get secrets from server
_secrethub_get_secrets() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
# Try to get secrets from server (with timeout)
secrethub secret list --format json 2>/dev/null | \
grep -o '"path":"[^"]*"' | \
cut -d'"' -f4 2>/dev/null || echo ""
fi
}
# Helper function to get policies from server
_secrethub_get_policies() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
# Try to get policies from server (with timeout)
secrethub policy list --format json 2>/dev/null | \
grep -o '"name":"[^"]*"' | \
cut -d'"' -f4 2>/dev/null || echo ""
fi
}
# Helper function to get agents from server
_secrethub_get_agents() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
# Try to get agents from server (with timeout)
secrethub agent list --format json 2>/dev/null | \
grep -o '"id":"[^"]*"' | \
cut -d'"' -f4 2>/dev/null || echo ""
fi
}
# Helper function to get policy templates
_secrethub_get_policy_templates() {
echo "admin read_only business_hours time_limited emergency_access"
}
complete -F _secrethub secrethub
"""
@zsh_completion """
#compdef secrethub
# Zsh completion for SecretHub CLI
# Generated by SecretHub.CLI.Completion
_secrethub() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a global_options
global_options=(
'(-h --help)'{-h,--help}'[Show help message]'
'(-v --version)'{-v,--version}'[Show version]'
'(-s --server)'{-s,--server}'[Server URL]:url:_urls'
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)'
'(-q --quiet)'{-q,--quiet}'[Suppress output]'
'--verbose[Detailed output]'
)
_arguments -C \
'1: :->command' \
'*::arg:->args' \
$global_options
case $state in
command)
local -a commands
commands=(
'login:Authenticate with SecretHub server'
'logout:Clear authentication credentials'
'whoami:Show current authentication status'
'secret:Manage secrets'
'policy:Manage access policies'
'agent:Manage SecretHub agents'
'config:Manage CLI configuration'
'completion:Generate shell completion scripts'
'help:Show help message'
'version:Show version information'
)
_describe 'secrethub commands' commands
;;
args)
case $line[1] in
login)
_arguments \
'--role-id[AppRole role ID]:role_id:' \
'--secret-id[AppRole secret ID]:secret_id:' \
$global_options
;;
logout|whoami)
_arguments $global_options
;;
secret)
_secrethub_secret_commands
;;
policy)
_secrethub_policy_commands
;;
agent)
_secrethub_agent_commands
;;
config)
_secrethub_config_commands
;;
completion)
_arguments \
'1:shell:(bash zsh)' \
$global_options
;;
esac
;;
esac
}
_secrethub_secret_commands() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a secret_commands
secret_commands=(
'list:List all secrets'
'get:Get a secret value'
'create:Create a new secret'
'update:Update an existing secret'
'delete:Delete a secret'
'versions:Show version history'
'rollback:Rollback to a previous version'
)
_arguments -C \
'1: :->subcmd' \
'*::arg:->args'
case $state in
subcmd)
_describe 'secret commands' secret_commands
;;
args)
case $line[1] in
list)
_arguments \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
get|update|delete|versions)
_arguments \
'1:path:_secrethub_complete_secrets' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
create)
_arguments \
'1:path:' \
'--value[Secret value]:value:' \
'--from-file[Read value from file]:file:_files' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
rollback)
_arguments \
'1:path:_secrethub_complete_secrets' \
'2:version:' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
esac
;;
esac
}
_secrethub_policy_commands() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a policy_commands
policy_commands=(
'list:List all policies'
'get:Get policy details'
'create:Create a new policy'
'update:Update a policy'
'delete:Delete a policy'
'simulate:Simulate policy evaluation'
'templates:List available policy templates'
)
_arguments -C \
'1: :->subcmd' \
'*::arg:->args'
case $state in
subcmd)
_describe 'policy commands' policy_commands
;;
args)
case $line[1] in
list|templates)
_arguments \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
get|delete|simulate)
_arguments \
'1:name:_secrethub_complete_policies' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
create)
_arguments \
'--from-template[Use policy template]:template:_secrethub_complete_policy_templates' \
'--name[Policy name]:name:' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
update)
_arguments \
'1:name:_secrethub_complete_policies' \
'--from-template[Use policy template]:template:_secrethub_complete_policy_templates' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
esac
;;
esac
}
_secrethub_agent_commands() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a agent_commands
agent_commands=(
'list:List connected agents'
'status:Get agent status'
'logs:Stream agent logs'
)
_arguments -C \
'1: :->subcmd' \
'*::arg:->args'
case $state in
subcmd)
_describe 'agent commands' agent_commands
;;
args)
case $line[1] in
list)
_arguments \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
status|logs)
_arguments \
'1:id:_secrethub_complete_agents' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
esac
;;
esac
}
_secrethub_config_commands() {
local curcontext="$curcontext" state line
typeset -A opt_args
local -a config_commands
config_commands=(
'list:List all configuration'
'get:Get configuration value'
'set:Set configuration value'
)
local -a config_keys
config_keys=(
'server_url:SecretHub server URL'
'default_format:Default output format'
'log_level:Logging level'
)
_arguments -C \
'1: :->subcmd' \
'*::arg:->args'
case $state in
subcmd)
_describe 'config commands' config_commands
;;
args)
case $line[1] in
list)
_arguments \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
get)
_arguments \
'1:key:_secrethub_complete_config_keys' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
set)
_arguments \
'1:key:_secrethub_complete_config_keys' \
'2:value:_secrethub_complete_config_value' \
'(-f --format)'{-f,--format}'[Output format]:format:(json table yaml)' \
'(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--verbose[Detailed output]'
;;
esac
;;
esac
}
# Completion helpers for dynamic data
_secrethub_complete_secrets() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
local secrets=(${(f)"$(secrethub secret list --format json 2>/dev/null | \
grep -o '"path":"[^"]*"' | cut -d'"' -f4)"})
_describe 'secret paths' secrets
fi
}
_secrethub_complete_policies() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
local policies=(${(f)"$(secrethub policy list --format json 2>/dev/null | \
grep -o '"name":"[^"]*"' | cut -d'"' -f4)"})
_describe 'policy names' policies
fi
}
_secrethub_complete_agents() {
local config_file="${HOME}/.secrethub/config.toml"
if [[ -f "${config_file}" ]]; then
local agents=(${(f)"$(secrethub agent list --format json 2>/dev/null | \
grep -o '"id":"[^"]*"' | cut -d'"' -f4)"})
_describe 'agent IDs' agents
fi
}
_secrethub_complete_policy_templates() {
local templates=(
'admin:Full administrative access'
'read_only:Read-only access to secrets'
'business_hours:Access restricted to business hours'
'time_limited:Time-limited access policy'
'emergency_access:Emergency break-glass access'
)
_describe 'policy templates' templates
}
_secrethub_complete_config_keys() {
local keys=(
'server_url:SecretHub server URL'
'default_format:Default output format (json|table|yaml)'
'log_level:Logging level (debug|info|warn|error)'
)
_describe 'configuration keys' keys
}
_secrethub_complete_config_value() {
local key="${line[2]}"
case "$key" in
default_format)
_values 'format' json table yaml
;;
log_level)
_values 'log level' debug info warn error
;;
server_url)
_urls
;;
esac
}
_secrethub "$@"
"""
@doc """
Generate completion script for the specified shell.
## Parameters
- shell: :bash or :zsh
## Returns
- {:ok, script} - The completion script content
- {:error, reason} - If shell is not supported
"""
def generate(:bash), do: {:ok, @bash_completion}
def generate(:zsh), do: {:ok, @zsh_completion}
def generate(shell), do: {:error, "Unsupported shell: #{shell}"}
@doc """
Write completion script to file.
## Parameters
- shell: :bash or :zsh
- path: Output file path
## Returns
- :ok on success
- {:error, reason} on failure
"""
def write_to_file(shell, path) do
with {:ok, script} <- generate(shell),
:ok <- File.mkdir_p(Path.dirname(path)),
:ok <- File.write(path, script) do
:ok
else
{:error, reason} -> {:error, reason}
end
end
@doc """
Get installation instructions for the specified shell.
"""
def installation_instructions(:bash) do
"""
Bash Completion Installation
=============================
Option 1: System-wide installation (requires sudo)
--------------------------------------------------
sudo secrethub completion bash > /etc/bash_completion.d/secrethub
source ~/.bashrc
Option 2: User-local installation
----------------------------------
mkdir -p ~/.bash_completion.d
secrethub completion bash > ~/.bash_completion.d/secrethub
Add to ~/.bashrc:
if [ -f ~/.bash_completion.d/secrethub ]; then
. ~/.bash_completion.d/secrethub
fi
source ~/.bashrc
Option 3: macOS with Homebrew
------------------------------
secrethub completion bash > $(brew --prefix)/etc/bash_completion.d/secrethub
source ~/.bash_profile
Testing
-------
After installation, test by typing:
secrethub sec<TAB>
Should complete to: secrethub secret
Troubleshooting
---------------
If completion doesn't work:
1. Ensure bash-completion is installed
- Debian/Ubuntu: sudo apt install bash-completion
- RHEL/CentOS: sudo yum install bash-completion
- macOS: brew install bash-completion
2. Restart your shell or source your rc file
3. Check that secrethub is in your PATH
"""
end
def installation_instructions(:zsh) do
"""
Zsh Completion Installation
============================
Option 1: User-local installation
----------------------------------
mkdir -p ~/.zsh/completion
secrethub completion zsh > ~/.zsh/completion/_secrethub
Add to ~/.zshrc (before compinit):
fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit
compinit
source ~/.zshrc
Option 2: System-wide installation (requires sudo)
---------------------------------------------------
sudo secrethub completion zsh > /usr/local/share/zsh/site-functions/_secrethub
Restart your shell
Option 3: oh-my-zsh
-------------------
mkdir -p ~/.oh-my-zsh/custom/plugins/secrethub
secrethub completion zsh > ~/.oh-my-zsh/custom/plugins/secrethub/_secrethub
Add 'secrethub' to plugins in ~/.zshrc:
plugins=(... secrethub)
source ~/.zshrc
Testing
-------
After installation, test by typing:
secrethub sec<TAB>
Should show: secret -- Manage secrets
Troubleshooting
---------------
If completion doesn't work:
1. Check that fpath includes your completion directory:
echo $fpath
2. Clear completion cache:
rm -f ~/.zcompdump*
compinit
3. Check that secrethub is in your PATH
4. Ensure compinit is called after adding to fpath
"""
end
def installation_instructions(shell) do
"Unsupported shell: #{shell}"
end
end