mirror of
https://github.com/ohmyzsh/ohmyzsh.git
synced 2026-04-10 16:32:00 +00:00
Compare commits
10 Commits
df7c1a32f6
...
2b713af60a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b713af60a | ||
|
|
7c10d9839f | ||
|
|
103246c198 | ||
|
|
887a864aba | ||
|
|
d1fb3ebfc7 | ||
|
|
beab0c0b3c | ||
|
|
ff601692ec | ||
|
|
7cc680d82c | ||
|
|
4ae29045f2 | ||
|
|
c3b2b797f0 |
2
.github/workflows/dependencies.yml
vendored
2
.github/workflows/dependencies.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
contents: write # this is needed to push commits and branches
|
contents: write # this is needed to push commits and branches
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
certifi==2026.2.25
|
certifi==2026.2.25
|
||||||
charset-normalizer==3.4.6
|
charset-normalizer==3.4.7
|
||||||
idna==3.11
|
idna==3.11
|
||||||
PyYAML==6.0.3
|
PyYAML==6.0.3
|
||||||
requests==2.33.0
|
requests==2.33.1
|
||||||
semver==3.0.4
|
semver==3.0.4
|
||||||
urllib3==2.6.3
|
urllib3==2.6.3
|
||||||
|
|||||||
4
.github/workflows/installer.yml
vendored
4
.github/workflows/installer.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- macos-latest
|
- macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
- test
|
- test
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/project.yml
vendored
2
.github/workflows/project.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
- name: Authenticate as @ohmyzsh
|
- name: Authenticate as @ohmyzsh
|
||||||
|
|||||||
2
.github/workflows/scorecard.yml
vendored
2
.github/workflows/scorecard.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden the runner (Audit all outbound calls)
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
149
plugins/keyman/README.md
Normal file
149
plugins/keyman/README.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# keyman plugin
|
||||||
|
|
||||||
|
Provides a unified `keyman` command for managing SSH and GPG keys from the terminal.
|
||||||
|
Works on macOS, Linux (X11/Wayland), and WSL.
|
||||||
|
|
||||||
|
> **Relationship to other plugins:** The [`ssh-agent`](../ssh-agent) and
|
||||||
|
> [`gpg-agent`](../gpg-agent) plugins manage agent *daemons* (auto-starting,
|
||||||
|
> identity loading, forwarding). `keyman` focuses on key *lifecycle* — creating,
|
||||||
|
> listing, copying, and deleting keys. They are complementary and can be used
|
||||||
|
> together.
|
||||||
|
|
||||||
|
To enable it, add `keyman` to your plugins:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
plugins=(... keyman)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then type `keyman help` to see all available commands.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
At least one of the following must be available:
|
||||||
|
|
||||||
|
- `ssh-keygen` — for SSH key commands
|
||||||
|
- `gpg` — for GPG key commands
|
||||||
|
|
||||||
|
### Clipboard support
|
||||||
|
|
||||||
|
For clipboard commands (`keyman ssh copy`, `keyman gpg copy`), one of the tools
|
||||||
|
below must be installed:
|
||||||
|
|
||||||
|
| Platform | Tool | Notes |
|
||||||
|
| --------------- | ---------- | -------------------------- |
|
||||||
|
| macOS | `pbcopy` | Built-in |
|
||||||
|
| Linux (X11) | `xclip` | `apt install xclip` |
|
||||||
|
| Linux (Wayland) | `wl-copy` | `apt install wl-clipboard` |
|
||||||
|
| WSL | `clip.exe` | Built-in (Windows side) |
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### SSH
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
| --------------------------------------------- | --------------------------------------- |
|
||||||
|
| `keyman ssh new [comment] [file] [type]` | Create a new SSH key (default: ed25519) |
|
||||||
|
| `keyman ssh ls` | List all SSH public keys in `~/.ssh` |
|
||||||
|
| `keyman ssh copy [pubkey_file]` | Copy a public key to clipboard |
|
||||||
|
| `keyman ssh rm <keyfile>` | Delete an SSH key pair |
|
||||||
|
|
||||||
|
### GPG
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
| -------------------------------------------------- | --------------------------------------------------------------- |
|
||||||
|
| `keyman gpg new` | Create a GPG key (interactive, via `gpg --full-generate-key`) |
|
||||||
|
| `keyman gpg quick-new "Name" "Email" [expiry]` | Create a GPG key non-interactively (ed25519, default 2y expiry) |
|
||||||
|
| `keyman gpg ls [-s\|--secret]` | List public keys, or secret keys with `-s` |
|
||||||
|
| `keyman gpg pub <id>` | Export a GPG public key (armored) |
|
||||||
|
| `keyman gpg priv <id>` | Export a GPG secret key (armored, with confirmation) |
|
||||||
|
| `keyman gpg copy <id>` | Copy a GPG public key to clipboard |
|
||||||
|
| `keyman gpg fp <id>` | Show a GPG key fingerprint |
|
||||||
|
| `keyman gpg rm <id>` | Delete a GPG key (secret + public) |
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
| -------------- | ----------------- |
|
||||||
|
| `keyman help` | Show help message |
|
||||||
|
|
||||||
|
## Tab Completion
|
||||||
|
|
||||||
|
All commands support multi-level Zsh tab completion:
|
||||||
|
|
||||||
|
```
|
||||||
|
keyman <TAB> → ssh | gpg | help
|
||||||
|
keyman ssh <TAB> → new | ls | copy | rm
|
||||||
|
keyman gpg <TAB> → new | quick-new | ls | pub | priv | copy | fp | rm
|
||||||
|
```
|
||||||
|
|
||||||
|
At the argument level:
|
||||||
|
|
||||||
|
- **`keyman ssh new`** — completes key types (`ed25519`, `rsa`, `ecdsa`) and file paths
|
||||||
|
- **`keyman ssh copy`** — completes `~/.ssh/*.pub` files
|
||||||
|
- **`keyman ssh rm`** — completes private key files in `~/.ssh`
|
||||||
|
- **`keyman gpg ls`** — completes `--secret` / `-s` options
|
||||||
|
- **`keyman gpg quick-new`** — completes common expiry values (`1y`, `2y`, `3y`, `5y`, `0`)
|
||||||
|
- **`keyman gpg pub`**, **`priv`**, **`copy`**, **`fp`**, **`rm`** — complete GPG key IDs and emails from your keyring
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
**IMPORTANT: put these settings _before_ the line that sources oh-my-zsh.**
|
||||||
|
|
||||||
|
### `lang`
|
||||||
|
|
||||||
|
Set the UI language. Supported values: `en` (default), `zh`.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zstyle :omz:plugins:keyman lang zh
|
||||||
|
```
|
||||||
|
|
||||||
|
### `debug`
|
||||||
|
|
||||||
|
Show a status message when the plugin loads:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zstyle :omz:plugins:keyman debug true
|
||||||
|
```
|
||||||
|
|
||||||
|
### `default-ssh-type`
|
||||||
|
|
||||||
|
Set the default SSH key type for `keyman ssh new`. Supported values:
|
||||||
|
`ed25519` (default), `rsa`, `ecdsa`.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zstyle :omz:plugins:keyman default-ssh-type rsa
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# Create a default ed25519 key
|
||||||
|
keyman ssh new
|
||||||
|
|
||||||
|
# Create an RSA key with a custom comment and path
|
||||||
|
keyman ssh new "me@work" ~/.ssh/work_key rsa
|
||||||
|
|
||||||
|
# List all SSH keys
|
||||||
|
keyman ssh ls
|
||||||
|
|
||||||
|
# Copy the default public key to clipboard
|
||||||
|
keyman ssh copy
|
||||||
|
|
||||||
|
# Delete an SSH key
|
||||||
|
keyman ssh rm ~/.ssh/id_ed25519
|
||||||
|
|
||||||
|
# Create a GPG key interactively
|
||||||
|
keyman gpg new
|
||||||
|
|
||||||
|
# Create a GPG key quickly
|
||||||
|
keyman gpg quick-new "John Doe" "john@example.com" 1y
|
||||||
|
|
||||||
|
# List GPG secret keys
|
||||||
|
keyman gpg ls --secret
|
||||||
|
|
||||||
|
# Export and copy a GPG public key
|
||||||
|
keyman gpg copy john@example.com
|
||||||
|
|
||||||
|
# Show GPG key fingerprint
|
||||||
|
keyman gpg fp john@example.com
|
||||||
|
```
|
||||||
765
plugins/keyman/keyman.plugin.zsh
Normal file
765
plugins/keyman/keyman.plugin.zsh
Normal file
@@ -0,0 +1,765 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
# keyman.plugin.zsh -- SSH & GPG key management plugin for oh-my-zsh
|
||||||
|
#
|
||||||
|
# Author: keyman contributors
|
||||||
|
# Version: 0.2.0
|
||||||
|
# License: MIT (same as oh-my-zsh)
|
||||||
|
#
|
||||||
|
# Usage: add 'keyman' to plugins in ~/.zshrc
|
||||||
|
# keyman ssh <action> [args...]
|
||||||
|
# keyman gpg <action> [args...]
|
||||||
|
# keyman help
|
||||||
|
#
|
||||||
|
# Configuration (in .zshrc, before plugins=(...)):
|
||||||
|
# zstyle ':omz:plugins:keyman' lang en # en (default) | zh
|
||||||
|
# zstyle ':omz:plugins:keyman' debug true # show load message (default: false)
|
||||||
|
# zstyle ':omz:plugins:keyman' default-ssh-type ed25519 # ed25519 | rsa | ecdsa
|
||||||
|
|
||||||
|
# Require at least one of ssh-keygen or gpg
|
||||||
|
(( $+commands[ssh-keygen] + $+commands[gpg] )) || return
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Persistent globals
|
||||||
|
# =====================================================
|
||||||
|
typeset -gA _km_msg
|
||||||
|
|
||||||
|
typeset -g _km_red=$'\033[0;31m'
|
||||||
|
typeset -g _km_green=$'\033[0;32m'
|
||||||
|
typeset -g _km_yellow=$'\033[0;33m'
|
||||||
|
typeset -g _km_blue=$'\033[0;34m'
|
||||||
|
typeset -g _km_cyan=$'\033[0;36m'
|
||||||
|
typeset -g _km_bold=$'\033[1m'
|
||||||
|
typeset -g _km_reset=$'\033[0m'
|
||||||
|
|
||||||
|
_km_info() { print -r -- "${_km_blue}[keyman]${_km_reset} $*" }
|
||||||
|
_km_ok() { print -r -- "${_km_green}[keyman] ✅${_km_reset} $*" }
|
||||||
|
_km_warn() { print -r -- "${_km_yellow}[keyman] ⚠️${_km_reset} $*" }
|
||||||
|
_km_err() { print -r -- "${_km_red}[keyman] ❌${_km_reset} $*" }
|
||||||
|
|
||||||
|
# Cross-platform clipboard
|
||||||
|
_km_copy_to_clipboard() {
|
||||||
|
local content="$1"
|
||||||
|
if command -v pbcopy &>/dev/null; then
|
||||||
|
printf '%s' "$content" | pbcopy
|
||||||
|
elif command -v xclip &>/dev/null; then
|
||||||
|
printf '%s' "$content" | xclip -selection clipboard
|
||||||
|
elif command -v wl-copy &>/dev/null; then
|
||||||
|
printf '%s' "$content" | wl-copy
|
||||||
|
elif command -v clip.exe &>/dev/null; then
|
||||||
|
printf '%s' "$content" | clip.exe
|
||||||
|
else
|
||||||
|
_km_warn "${_km_msg[clipboard_not_found]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Initialization (scoped via anonymous function)
|
||||||
|
# =====================================================
|
||||||
|
function {
|
||||||
|
local lang
|
||||||
|
zstyle -s ':omz:plugins:keyman' lang lang || lang=en
|
||||||
|
|
||||||
|
case "$lang" in
|
||||||
|
zh)
|
||||||
|
_km_msg=(
|
||||||
|
# -- clipboard --
|
||||||
|
clipboard_not_found "未找到剪贴板工具 (pbcopy/xclip/wl-copy/clip.exe)"
|
||||||
|
# -- general --
|
||||||
|
cancelled "已取消"
|
||||||
|
deleted "已删除"
|
||||||
|
confirm_delete "确认删除?(y/N) "
|
||||||
|
about_to_delete "即将删除:"
|
||||||
|
label_private_key "私钥:"
|
||||||
|
label_public_key "公钥:"
|
||||||
|
label_pubkey_content "公钥内容:"
|
||||||
|
label_file "文件:"
|
||||||
|
label_type "类型:"
|
||||||
|
label_fingerprint "指纹:"
|
||||||
|
label_comment "注释:"
|
||||||
|
# -- ssh new --
|
||||||
|
ssh_dir_created "已创建 ~/.ssh 目录"
|
||||||
|
file_exists "文件已存在"
|
||||||
|
confirm_overwrite "是否覆盖?(y/N) "
|
||||||
|
creating_key "正在创建 %s 密钥..."
|
||||||
|
unsupported_key_type "不支持的密钥类型: %s (可选: ed25519, rsa, ecdsa)"
|
||||||
|
key_created "密钥已创建"
|
||||||
|
added_to_agent "已添加到 ssh-agent"
|
||||||
|
key_creation_failed "密钥创建失败"
|
||||||
|
# -- ssh ls --
|
||||||
|
ssh_dir_not_found "~/.ssh 目录不存在"
|
||||||
|
no_ssh_keys_found "未找到任何 SSH 公钥"
|
||||||
|
# -- ssh copy --
|
||||||
|
pubkey_not_found "公钥文件不存在"
|
||||||
|
available_pubkeys "可用的公钥:"
|
||||||
|
none "(无)"
|
||||||
|
pubkey_copied "公钥已复制到剪贴板"
|
||||||
|
# -- ssh rm --
|
||||||
|
usage_ssh_rm "用法: keyman ssh rm <keyfile>"
|
||||||
|
key_not_found "密钥不存在"
|
||||||
|
# -- gpg quick-new --
|
||||||
|
usage_gpg_quick_new "用法: keyman gpg quick-new \"姓名\" \"邮箱\" [过期时间]"
|
||||||
|
email_has_gpg_key "该邮箱已有 GPG 密钥:"
|
||||||
|
confirm_create_new "继续创建新密钥?(y/N) "
|
||||||
|
creating_gpg_key "正在创建 GPG 密钥..."
|
||||||
|
label_name " 姓名:"
|
||||||
|
label_email " 邮箱:"
|
||||||
|
label_expiry " 过期:"
|
||||||
|
gpg_key_created "GPG 密钥已创建"
|
||||||
|
gpg_key_creation_failed "GPG 密钥创建失败"
|
||||||
|
# -- gpg ls --
|
||||||
|
gpg_secret_key_list "GPG 私钥列表:"
|
||||||
|
gpg_public_key_list "GPG 公钥列表:"
|
||||||
|
# -- gpg pub --
|
||||||
|
usage_gpg_pub "用法: keyman gpg pub <邮箱或KeyID>"
|
||||||
|
gpg_key_not_found "未找到密钥"
|
||||||
|
gpg_public_key "GPG 公钥"
|
||||||
|
# -- gpg priv --
|
||||||
|
usage_gpg_priv "用法: keyman gpg priv <邮箱或KeyID>"
|
||||||
|
gpg_secret_not_found "未找到私钥"
|
||||||
|
warn_export_secret "即将导出私钥!请确保在安全环境下操作"
|
||||||
|
confirm_export "确认导出?(y/N) "
|
||||||
|
# -- gpg copy --
|
||||||
|
usage_gpg_copy "用法: keyman gpg copy <邮箱或KeyID>"
|
||||||
|
gpg_pubkey_copied "GPG 公钥已复制到剪贴板"
|
||||||
|
# -- gpg fp --
|
||||||
|
usage_gpg_fp "用法: keyman gpg fp <邮箱或KeyID>"
|
||||||
|
# -- gpg rm --
|
||||||
|
usage_gpg_rm "用法: keyman gpg rm <邮箱或KeyID>"
|
||||||
|
about_to_delete_gpg "即将删除 GPG 密钥"
|
||||||
|
key_info "密钥信息:"
|
||||||
|
# -- errors --
|
||||||
|
unknown_group "未知命令组: %s (可用: ssh, gpg)"
|
||||||
|
unknown_ssh_action "未知 SSH 操作: %s (可用: new, ls, copy, rm)"
|
||||||
|
unknown_gpg_action "未知 GPG 操作: %s (可用: new, quick-new, ls, pub, priv, copy, fp, rm)"
|
||||||
|
# -- debug --
|
||||||
|
loaded "已加载,输入 keyman help 查看帮助"
|
||||||
|
# -- help --
|
||||||
|
help_text \
|
||||||
|
"${_km_bold}keyman${_km_reset} — SSH & GPG 密钥管理
|
||||||
|
|
||||||
|
${_km_cyan}用法:${_km_reset}
|
||||||
|
keyman <命令组> <操作> [参数...]
|
||||||
|
keyman help
|
||||||
|
|
||||||
|
${_km_cyan}SSH 命令:${_km_reset}
|
||||||
|
keyman ssh new [comment] [file] [type] 创建 SSH 密钥
|
||||||
|
keyman ssh ls 列出所有 SSH 公钥
|
||||||
|
keyman ssh copy [pubkey_file] 复制公钥到剪贴板
|
||||||
|
keyman ssh rm <keyfile> 删除 SSH 密钥对
|
||||||
|
|
||||||
|
${_km_cyan}GPG 命令:${_km_reset}
|
||||||
|
keyman gpg new 创建密钥(交互式)
|
||||||
|
keyman gpg quick-new \"姓名\" \"邮箱\" [期限] 创建密钥(快速)
|
||||||
|
keyman gpg ls [-s|--secret] 列出密钥
|
||||||
|
keyman gpg pub <id> 导出公钥
|
||||||
|
keyman gpg priv <id> 导出私钥
|
||||||
|
keyman gpg copy <id> 复制公钥到剪贴板
|
||||||
|
keyman gpg fp <id> 查看指纹
|
||||||
|
keyman gpg rm <id> 删除密钥"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_km_msg=(
|
||||||
|
# -- clipboard --
|
||||||
|
clipboard_not_found "Clipboard tool not found (pbcopy/xclip/wl-copy/clip.exe)"
|
||||||
|
# -- general --
|
||||||
|
cancelled "Cancelled"
|
||||||
|
deleted "Deleted"
|
||||||
|
confirm_delete "Confirm deletion? (y/N) "
|
||||||
|
about_to_delete "About to delete:"
|
||||||
|
label_private_key "Private key:"
|
||||||
|
label_public_key "Public key:"
|
||||||
|
label_pubkey_content "Public key content:"
|
||||||
|
label_file "File:"
|
||||||
|
label_type "Type:"
|
||||||
|
label_fingerprint "Fingerprint:"
|
||||||
|
label_comment "Comment:"
|
||||||
|
# -- ssh new --
|
||||||
|
ssh_dir_created "Created ~/.ssh directory"
|
||||||
|
file_exists "File already exists"
|
||||||
|
confirm_overwrite "Overwrite? (y/N) "
|
||||||
|
creating_key "Creating %s key..."
|
||||||
|
unsupported_key_type "Unsupported key type: %s (supported: ed25519, rsa, ecdsa)"
|
||||||
|
key_created "Key created"
|
||||||
|
added_to_agent "Added to ssh-agent"
|
||||||
|
key_creation_failed "Key creation failed"
|
||||||
|
# -- ssh ls --
|
||||||
|
ssh_dir_not_found "~/.ssh directory does not exist"
|
||||||
|
no_ssh_keys_found "No SSH public keys found"
|
||||||
|
# -- ssh copy --
|
||||||
|
pubkey_not_found "Public key file not found"
|
||||||
|
available_pubkeys "Available public keys:"
|
||||||
|
none "(none)"
|
||||||
|
pubkey_copied "Public key copied to clipboard"
|
||||||
|
# -- ssh rm --
|
||||||
|
usage_ssh_rm "Usage: keyman ssh rm <keyfile>"
|
||||||
|
key_not_found "Key not found"
|
||||||
|
# -- gpg quick-new --
|
||||||
|
usage_gpg_quick_new "Usage: keyman gpg quick-new \"Name\" \"Email\" [exp]"
|
||||||
|
email_has_gpg_key "This email already has a GPG key:"
|
||||||
|
confirm_create_new "Continue creating new key? (y/N) "
|
||||||
|
creating_gpg_key "Creating GPG key..."
|
||||||
|
label_name " Name:"
|
||||||
|
label_email " Email:"
|
||||||
|
label_expiry " Expiry:"
|
||||||
|
gpg_key_created "GPG key created"
|
||||||
|
gpg_key_creation_failed "GPG key creation failed"
|
||||||
|
# -- gpg ls --
|
||||||
|
gpg_secret_key_list "GPG secret key list:"
|
||||||
|
gpg_public_key_list "GPG public key list:"
|
||||||
|
# -- gpg pub --
|
||||||
|
usage_gpg_pub "Usage: keyman gpg pub <email-or-KeyID>"
|
||||||
|
gpg_key_not_found "Key not found"
|
||||||
|
gpg_public_key "GPG public key"
|
||||||
|
# -- gpg priv --
|
||||||
|
usage_gpg_priv "Usage: keyman gpg priv <email-or-KeyID>"
|
||||||
|
gpg_secret_not_found "Secret key not found"
|
||||||
|
warn_export_secret "About to export secret key! Make sure you are in a secure environment"
|
||||||
|
confirm_export "Confirm export? (y/N) "
|
||||||
|
# -- gpg copy --
|
||||||
|
usage_gpg_copy "Usage: keyman gpg copy <email-or-KeyID>"
|
||||||
|
gpg_pubkey_copied "GPG public key copied to clipboard"
|
||||||
|
# -- gpg fp --
|
||||||
|
usage_gpg_fp "Usage: keyman gpg fp <email-or-KeyID>"
|
||||||
|
# -- gpg rm --
|
||||||
|
usage_gpg_rm "Usage: keyman gpg rm <email-or-KeyID>"
|
||||||
|
about_to_delete_gpg "About to delete GPG key"
|
||||||
|
key_info "Key info:"
|
||||||
|
# -- errors --
|
||||||
|
unknown_group "Unknown command group: %s (available: ssh, gpg)"
|
||||||
|
unknown_ssh_action "Unknown SSH action: %s (available: new, ls, copy, rm)"
|
||||||
|
unknown_gpg_action "Unknown GPG action: %s (available: new, quick-new, ls, pub, priv, copy, fp, rm)"
|
||||||
|
# -- debug --
|
||||||
|
loaded "Loaded. Type 'keyman help' for help"
|
||||||
|
# -- help --
|
||||||
|
help_text \
|
||||||
|
"${_km_bold}keyman${_km_reset} — SSH & GPG Key Manager
|
||||||
|
|
||||||
|
${_km_cyan}Usage:${_km_reset}
|
||||||
|
keyman <group> <action> [args...]
|
||||||
|
keyman help
|
||||||
|
|
||||||
|
${_km_cyan}SSH Commands:${_km_reset}
|
||||||
|
keyman ssh new [comment] [file] [type] Create SSH key
|
||||||
|
keyman ssh ls List SSH public keys
|
||||||
|
keyman ssh copy [pubkey_file] Copy pubkey to clipboard
|
||||||
|
keyman ssh rm <keyfile> Delete SSH key pair
|
||||||
|
|
||||||
|
${_km_cyan}GPG Commands:${_km_reset}
|
||||||
|
keyman gpg new Create key (interactive)
|
||||||
|
keyman gpg quick-new \"Name\" \"Email\" [exp] Create key (quick)
|
||||||
|
keyman gpg ls [-s|--secret] List keys
|
||||||
|
keyman gpg pub <id> Export public key
|
||||||
|
keyman gpg priv <id> Export secret key
|
||||||
|
keyman gpg copy <id> Copy pubkey to clipboard
|
||||||
|
keyman gpg fp <id> Show fingerprint
|
||||||
|
keyman gpg rm <id> Delete key"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Debug output
|
||||||
|
zstyle -t ':omz:plugins:keyman' debug && _km_info "${_km_msg[loaded]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# SSH Actions
|
||||||
|
# =====================================================
|
||||||
|
|
||||||
|
# keyman ssh new [comment] [keyfile] [type]
|
||||||
|
_km_ssh_new() {
|
||||||
|
local comment="${1:-${USER:-$(whoami)}@${HOST:-$(hostname)}}"
|
||||||
|
local keyfile="${2:-}"
|
||||||
|
local keytype="${3:-}"
|
||||||
|
|
||||||
|
if [[ -z "$keytype" ]]; then
|
||||||
|
zstyle -s ':omz:plugins:keyman' default-ssh-type keytype || keytype=ed25519
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default path by type
|
||||||
|
if [[ -z "$keyfile" ]]; then
|
||||||
|
case "$keytype" in
|
||||||
|
rsa) keyfile="$HOME/.ssh/id_rsa" ;;
|
||||||
|
ecdsa) keyfile="$HOME/.ssh/id_ecdsa" ;;
|
||||||
|
ed25519) keyfile="$HOME/.ssh/id_ed25519" ;;
|
||||||
|
*) keyfile="$HOME/.ssh/id_${keytype}" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure .ssh directory exists
|
||||||
|
if [[ ! -d "$HOME/.ssh" ]]; then
|
||||||
|
mkdir -p "$HOME/.ssh"
|
||||||
|
chmod 700 "$HOME/.ssh"
|
||||||
|
_km_info "${_km_msg[ssh_dir_created]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prevent overwriting existing key
|
||||||
|
if [[ -f "$keyfile" ]]; then
|
||||||
|
_km_warn "${_km_msg[file_exists]}: $keyfile"
|
||||||
|
read -q "REPLY?${_km_msg[confirm_overwrite]}"
|
||||||
|
echo
|
||||||
|
if [[ "$REPLY" != "y" ]]; then
|
||||||
|
_km_info "${_km_msg[cancelled]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_km_info "$(printf "${_km_msg[creating_key]}" "$keytype")"
|
||||||
|
|
||||||
|
local _km_rc=0
|
||||||
|
case "$keytype" in
|
||||||
|
rsa)
|
||||||
|
ssh-keygen -t rsa -b 4096 -C "$comment" -f "$keyfile" || _km_rc=$?
|
||||||
|
;;
|
||||||
|
ecdsa)
|
||||||
|
ssh-keygen -t ecdsa -b 521 -C "$comment" -f "$keyfile" || _km_rc=$?
|
||||||
|
;;
|
||||||
|
ed25519)
|
||||||
|
ssh-keygen -t ed25519 -C "$comment" -f "$keyfile" || _km_rc=$?
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_km_err "$(printf "${_km_msg[unsupported_key_type]}" "$keytype")"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ $_km_rc -eq 0 && -f "${keyfile}.pub" ]]; then
|
||||||
|
chmod 600 "$keyfile"
|
||||||
|
chmod 644 "${keyfile}.pub"
|
||||||
|
|
||||||
|
_km_ok "${_km_msg[key_created]}"
|
||||||
|
print ""
|
||||||
|
print -r -- "${_km_cyan}${_km_msg[label_private_key]}${_km_reset} $keyfile"
|
||||||
|
print -r -- "${_km_cyan}${_km_msg[label_public_key]}${_km_reset} ${keyfile}.pub"
|
||||||
|
print ""
|
||||||
|
print -r -- "${_km_cyan}${_km_msg[label_pubkey_content]}${_km_reset}"
|
||||||
|
cat "${keyfile}.pub"
|
||||||
|
|
||||||
|
# Add to ssh-agent (start agent if needed)
|
||||||
|
if [[ -z "$SSH_AUTH_SOCK" ]]; then
|
||||||
|
eval "$(ssh-agent -s)" >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
if ssh-add "$keyfile" 2>/dev/null; then
|
||||||
|
_km_ok "${_km_msg[added_to_agent]}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_km_err "${_km_msg[key_creation_failed]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman ssh ls
|
||||||
|
_km_ssh_ls() {
|
||||||
|
local found=0
|
||||||
|
|
||||||
|
if [[ ! -d "$HOME/.ssh" ]]; then
|
||||||
|
_km_warn "${_km_msg[ssh_dir_not_found]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pubkey in "$HOME"/.ssh/*.pub(N); do
|
||||||
|
found=1
|
||||||
|
local info
|
||||||
|
info=$(ssh-keygen -l -f "$pubkey" 2>/dev/null) || continue
|
||||||
|
local bits=${info%% *}
|
||||||
|
local rest=${info#* }
|
||||||
|
local fingerprint=${rest%% *}
|
||||||
|
rest=${rest#* }
|
||||||
|
local keytype=${rest##* }
|
||||||
|
local comment=${rest% *}
|
||||||
|
|
||||||
|
print -r -- "${_km_cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${_km_reset}"
|
||||||
|
print -r -- "${_km_green}${_km_msg[label_file]}${_km_reset} $pubkey"
|
||||||
|
print -r -- "${_km_green}${_km_msg[label_type]}${_km_reset} ${keytype} (${bits} bits)"
|
||||||
|
print -r -- "${_km_green}${_km_msg[label_fingerprint]}${_km_reset} $fingerprint"
|
||||||
|
print -r -- "${_km_green}${_km_msg[label_comment]}${_km_reset} $comment"
|
||||||
|
print -r -- "${_km_green}${_km_msg[label_pubkey_content]}${_km_reset}"
|
||||||
|
cat "$pubkey"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $found -eq 0 ]]; then
|
||||||
|
_km_warn "${_km_msg[no_ssh_keys_found]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman ssh copy [keyfile]
|
||||||
|
_km_ssh_copy() {
|
||||||
|
local pubkey="${1:-$HOME/.ssh/id_ed25519.pub}"
|
||||||
|
|
||||||
|
[[ "$pubkey" != *.pub ]] && pubkey="${pubkey}.pub"
|
||||||
|
|
||||||
|
if [[ ! -f "$pubkey" ]]; then
|
||||||
|
_km_err "${_km_msg[pubkey_not_found]}: $pubkey"
|
||||||
|
_km_info "${_km_msg[available_pubkeys]}"
|
||||||
|
ls "$HOME"/.ssh/*.pub 2>/dev/null || echo " ${_km_msg[none]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local content=$(cat "$pubkey")
|
||||||
|
if _km_copy_to_clipboard "$content"; then
|
||||||
|
_km_ok "${_km_msg[pubkey_copied]}: $pubkey"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman ssh rm <keyfile>
|
||||||
|
_km_ssh_rm() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_ssh_rm]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local keyfile="$1"
|
||||||
|
|
||||||
|
keyfile="${keyfile%.pub}"
|
||||||
|
|
||||||
|
if [[ ! -f "$keyfile" && ! -f "${keyfile}.pub" ]]; then
|
||||||
|
_km_err "${_km_msg[key_not_found]}: $keyfile"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_km_warn "${_km_msg[about_to_delete]}"
|
||||||
|
[[ -f "$keyfile" ]] && echo " ${_km_msg[label_private_key]} $keyfile"
|
||||||
|
[[ -f "${keyfile}.pub" ]] && echo " ${_km_msg[label_public_key]} ${keyfile}.pub"
|
||||||
|
|
||||||
|
read -q "REPLY?${_km_msg[confirm_delete]}"
|
||||||
|
echo
|
||||||
|
if [[ "$REPLY" == "y" ]]; then
|
||||||
|
ssh-add -d "$keyfile" 2>/dev/null
|
||||||
|
[[ -f "$keyfile" ]] && rm -f "$keyfile"
|
||||||
|
[[ -f "${keyfile}.pub" ]] && rm -f "${keyfile}.pub"
|
||||||
|
_km_ok "${_km_msg[deleted]}"
|
||||||
|
else
|
||||||
|
_km_info "${_km_msg[cancelled]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# GPG Actions
|
||||||
|
# =====================================================
|
||||||
|
|
||||||
|
# keyman gpg new (interactive)
|
||||||
|
_km_gpg_new() {
|
||||||
|
gpg --full-generate-key
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg quick-new "Name" "Email" [expiry]
|
||||||
|
_km_gpg_quick_new() {
|
||||||
|
if [[ -z "${1:-}" || -z "${2:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_quick_new]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local name="$1"
|
||||||
|
local email="$2"
|
||||||
|
local expire="${3:-2y}"
|
||||||
|
|
||||||
|
if gpg --list-keys "$email" &>/dev/null; then
|
||||||
|
_km_warn "${_km_msg[email_has_gpg_key]}"
|
||||||
|
gpg --list-keys "$email"
|
||||||
|
read -q "REPLY?${_km_msg[confirm_create_new]}"
|
||||||
|
echo
|
||||||
|
[[ "$REPLY" != "y" ]] && return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_km_info "${_km_msg[creating_gpg_key]}"
|
||||||
|
_km_info "${_km_msg[label_name]} $name"
|
||||||
|
_km_info "${_km_msg[label_email]} $email"
|
||||||
|
_km_info "${_km_msg[label_expiry]} $expire"
|
||||||
|
|
||||||
|
local _km_rc=0
|
||||||
|
gpg --batch --gen-key <<EOF || _km_rc=$?
|
||||||
|
Key-Type: eddsa
|
||||||
|
Key-Curve: ed25519
|
||||||
|
Subkey-Type: ecdh
|
||||||
|
Subkey-Curve: cv25519
|
||||||
|
Name-Real: ${name}
|
||||||
|
Name-Email: ${email}
|
||||||
|
Expire-Date: ${expire}
|
||||||
|
%ask-passphrase
|
||||||
|
%commit
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ $_km_rc -eq 0 ]]; then
|
||||||
|
_km_ok "${_km_msg[gpg_key_created]}"
|
||||||
|
echo ""
|
||||||
|
gpg --list-keys --keyid-format long "$email"
|
||||||
|
else
|
||||||
|
_km_err "${_km_msg[gpg_key_creation_failed]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg ls [--secret|-s]
|
||||||
|
_km_gpg_ls() {
|
||||||
|
if [[ "$1" == "--secret" || "$1" == "-s" ]]; then
|
||||||
|
_km_info "${_km_msg[gpg_secret_key_list]}"
|
||||||
|
echo ""
|
||||||
|
gpg --list-secret-keys --keyid-format long
|
||||||
|
else
|
||||||
|
_km_info "${_km_msg[gpg_public_key_list]}"
|
||||||
|
echo ""
|
||||||
|
gpg --list-keys --keyid-format long
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg pub <email-or-KeyID>
|
||||||
|
_km_gpg_pub() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_pub]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local key_id="$1"
|
||||||
|
|
||||||
|
if ! gpg --list-keys "$key_id" &>/dev/null; then
|
||||||
|
_km_err "${_km_msg[gpg_key_not_found]}: $key_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_km_info "${_km_msg[gpg_public_key]} ($key_id):"
|
||||||
|
echo ""
|
||||||
|
gpg --armor --export "$key_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg priv <email-or-KeyID>
|
||||||
|
_km_gpg_priv() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_priv]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local key_id="$1"
|
||||||
|
|
||||||
|
if ! gpg --list-secret-keys "$key_id" &>/dev/null; then
|
||||||
|
_km_err "${_km_msg[gpg_secret_not_found]}: $key_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_km_warn "${_km_msg[warn_export_secret]}"
|
||||||
|
read -q "REPLY?${_km_msg[confirm_export]}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$REPLY" == "y" ]]; then
|
||||||
|
gpg --armor --export-secret-keys "$key_id"
|
||||||
|
else
|
||||||
|
_km_info "${_km_msg[cancelled]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg copy <email-or-KeyID>
|
||||||
|
_km_gpg_copy() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_copy]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local key_id="$1"
|
||||||
|
|
||||||
|
if ! gpg --list-keys "$key_id" &>/dev/null; then
|
||||||
|
_km_err "${_km_msg[gpg_key_not_found]}: $key_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local content=$(gpg --armor --export "$key_id")
|
||||||
|
if _km_copy_to_clipboard "$content"; then
|
||||||
|
_km_ok "${_km_msg[gpg_pubkey_copied]} ($key_id)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg fp <email-or-KeyID>
|
||||||
|
_km_gpg_fp() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_fp]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
gpg --fingerprint "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# keyman gpg rm <email-or-KeyID>
|
||||||
|
_km_gpg_rm() {
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
_km_err "${_km_msg[usage_gpg_rm]}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local key_id="$1"
|
||||||
|
|
||||||
|
_km_warn "${_km_msg[about_to_delete_gpg]}: $key_id"
|
||||||
|
_km_info "${_km_msg[key_info]}"
|
||||||
|
gpg --list-keys "$key_id" 2>/dev/null
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -q "REPLY?${_km_msg[confirm_delete]}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$REPLY" == "y" ]]; then
|
||||||
|
gpg --delete-secret-and-public-key "$key_id"
|
||||||
|
_km_ok "${_km_msg[deleted]}"
|
||||||
|
else
|
||||||
|
_km_info "${_km_msg[cancelled]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Main dispatcher
|
||||||
|
# =====================================================
|
||||||
|
keyman() {
|
||||||
|
local group="${1:-help}"
|
||||||
|
local action="${2:-}"
|
||||||
|
shift 2 2>/dev/null
|
||||||
|
|
||||||
|
case "$group" in
|
||||||
|
help|-h|--help)
|
||||||
|
print -r -- "${_km_msg[help_text]}"
|
||||||
|
;;
|
||||||
|
ssh)
|
||||||
|
case "$action" in
|
||||||
|
new) _km_ssh_new "$@" ;;
|
||||||
|
ls) _km_ssh_ls "$@" ;;
|
||||||
|
copy) _km_ssh_copy "$@" ;;
|
||||||
|
rm) _km_ssh_rm "$@" ;;
|
||||||
|
*)
|
||||||
|
_km_err "$(printf "${_km_msg[unknown_ssh_action]}" "$action")"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
gpg)
|
||||||
|
case "$action" in
|
||||||
|
new) _km_gpg_new "$@" ;;
|
||||||
|
quick-new) _km_gpg_quick_new "$@" ;;
|
||||||
|
ls) _km_gpg_ls "$@" ;;
|
||||||
|
pub) _km_gpg_pub "$@" ;;
|
||||||
|
priv) _km_gpg_priv "$@" ;;
|
||||||
|
copy) _km_gpg_copy "$@" ;;
|
||||||
|
fp) _km_gpg_fp "$@" ;;
|
||||||
|
rm) _km_gpg_rm "$@" ;;
|
||||||
|
*)
|
||||||
|
_km_err "$(printf "${_km_msg[unknown_gpg_action]}" "$action")"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_km_err "$(printf "${_km_msg[unknown_group]}" "$group")"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Zsh Completions
|
||||||
|
# =====================================================
|
||||||
|
_keyman() {
|
||||||
|
local curcontext="$curcontext" state line
|
||||||
|
typeset -A opt_args
|
||||||
|
|
||||||
|
_arguments -C \
|
||||||
|
'1:command group:->group' \
|
||||||
|
'*::args:->args'
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
group)
|
||||||
|
local -a groups=(
|
||||||
|
'ssh:Manage SSH keys'
|
||||||
|
'gpg:Manage GPG keys'
|
||||||
|
'help:Show help message'
|
||||||
|
)
|
||||||
|
_describe 'command group' groups
|
||||||
|
;;
|
||||||
|
args)
|
||||||
|
case "${line[1]}" in
|
||||||
|
ssh)
|
||||||
|
_arguments -C \
|
||||||
|
'1:ssh action:->ssh_action' \
|
||||||
|
'*::ssh_args:->ssh_args'
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
ssh_action)
|
||||||
|
local -a actions=(
|
||||||
|
'new:Create SSH key'
|
||||||
|
'ls:List SSH public keys'
|
||||||
|
'copy:Copy public key to clipboard'
|
||||||
|
'rm:Delete SSH key pair'
|
||||||
|
)
|
||||||
|
_describe 'ssh action' actions
|
||||||
|
;;
|
||||||
|
ssh_args)
|
||||||
|
case "${line[1]}" in
|
||||||
|
new)
|
||||||
|
_arguments \
|
||||||
|
'1:comment:' \
|
||||||
|
'2:keyfile:_files -W "$HOME/.ssh"' \
|
||||||
|
'3:key type:(ed25519 rsa ecdsa)'
|
||||||
|
;;
|
||||||
|
copy)
|
||||||
|
_arguments '1:public key file:_files -W "$HOME/.ssh" -g "*.pub"'
|
||||||
|
;;
|
||||||
|
rm)
|
||||||
|
local -a keys
|
||||||
|
if [[ -d "$HOME/.ssh" ]]; then
|
||||||
|
keys=("$HOME"/.ssh/id_*(N:t))
|
||||||
|
keys=(${keys:#*.pub})
|
||||||
|
fi
|
||||||
|
_describe 'SSH key' keys
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
gpg)
|
||||||
|
_arguments -C \
|
||||||
|
'1:gpg action:->gpg_action' \
|
||||||
|
'*::gpg_args:->gpg_args'
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
gpg_action)
|
||||||
|
local -a actions=(
|
||||||
|
'new:Create GPG key (interactive)'
|
||||||
|
'quick-new:Create GPG key (quick)'
|
||||||
|
'ls:List GPG keys'
|
||||||
|
'pub:Export public key'
|
||||||
|
'priv:Export secret key'
|
||||||
|
'copy:Copy public key to clipboard'
|
||||||
|
'fp:Show fingerprint'
|
||||||
|
'rm:Delete GPG key'
|
||||||
|
)
|
||||||
|
_describe 'gpg action' actions
|
||||||
|
;;
|
||||||
|
gpg_args)
|
||||||
|
case "${line[1]}" in
|
||||||
|
ls)
|
||||||
|
_arguments '1:option:(--secret -s)'
|
||||||
|
;;
|
||||||
|
quick-new)
|
||||||
|
_arguments \
|
||||||
|
'1:name:' \
|
||||||
|
'2:email:' \
|
||||||
|
'3:expiry:(1y 2y 3y 5y 0)'
|
||||||
|
;;
|
||||||
|
pub|priv|copy|fp|rm)
|
||||||
|
local -a key_ids
|
||||||
|
key_ids=(${(f)"$(gpg --list-keys --with-colons 2>/dev/null | awk -F: '
|
||||||
|
/^pub/ { keyid = $5 }
|
||||||
|
/^uid/ && keyid != "" {
|
||||||
|
uid = $10
|
||||||
|
if (uid != "") {
|
||||||
|
print keyid ":" uid
|
||||||
|
n = index(uid, "<")
|
||||||
|
if (n > 0) {
|
||||||
|
email = substr(uid, n + 1)
|
||||||
|
p = index(email, ">")
|
||||||
|
if (p > 0) email = substr(email, 1, p - 1)
|
||||||
|
if (email != "") print email ":" uid
|
||||||
|
}
|
||||||
|
keyid = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
')"})
|
||||||
|
_describe 'GPG key ID or email' key_ids
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
compdef _keyman keyman
|
||||||
@@ -30,14 +30,20 @@ function tofu_version_prompt_info() {
|
|||||||
alias tt='tofu'
|
alias tt='tofu'
|
||||||
alias tta='tofu apply'
|
alias tta='tofu apply'
|
||||||
alias tta!='tofu apply -auto-approve'
|
alias tta!='tofu apply -auto-approve'
|
||||||
|
alias ttap='tofu apply -parallelism=1'
|
||||||
|
alias ttapp='tofu apply tfplan'
|
||||||
alias ttc='tofu console'
|
alias ttc='tofu console'
|
||||||
alias ttd='tofu destroy'
|
alias ttd='tofu destroy'
|
||||||
alias ttd!='tofu destroy -auto-approve'
|
alias ttd!='tofu destroy -auto-approve'
|
||||||
alias ttf='tofu fmt'
|
alias ttf='tofu fmt'
|
||||||
alias ttfr='tofu fmt -recursive'
|
alias ttfr='tofu fmt -recursive'
|
||||||
alias tti='tofu init'
|
alias tti='tofu init'
|
||||||
|
alias ttir='tofu init -reconfigure'
|
||||||
|
alias ttiu='tofu init -upgrade'
|
||||||
|
alias ttiur='tofu init -upgrade -reconfigure'
|
||||||
alias tto='tofu output'
|
alias tto='tofu output'
|
||||||
alias ttp='tofu plan'
|
alias ttp='tofu plan'
|
||||||
|
alias ttpo='tofu plan -out tfplan'
|
||||||
alias ttv='tofu validate'
|
alias ttv='tofu validate'
|
||||||
alias tts='tofu state'
|
alias tts='tofu state'
|
||||||
alias ttsh='tofu show'
|
alias ttsh='tofu show'
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ function tf_prompt_info() {
|
|||||||
# check if in terraform dir and file exists
|
# check if in terraform dir and file exists
|
||||||
[[ -d "${TF_DATA_DIR:-.terraform}" && -r "${TF_DATA_DIR:-.terraform}/environment" ]] || return
|
[[ -d "${TF_DATA_DIR:-.terraform}" && -r "${TF_DATA_DIR:-.terraform}/environment" ]] || return
|
||||||
|
|
||||||
local workspace="$(< "${TF_DATA_DIR:-.terraform}/environment")"
|
local workspace="$(<"${TF_DATA_DIR:-.terraform}/environment")"
|
||||||
echo "${ZSH_THEME_TF_PROMPT_PREFIX-[}${workspace:gs/%/%%}${ZSH_THEME_TF_PROMPT_SUFFIX-]}"
|
echo "${ZSH_THEME_TF_PROMPT_PREFIX-[}${workspace:gs/%/%%}${ZSH_THEME_TF_PROMPT_SUFFIX-]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function tf_version_prompt_info() {
|
function tf_version_prompt_info() {
|
||||||
local terraform_version
|
local terraform_version
|
||||||
terraform_version=$(terraform --version | head -n 1 | cut -d ' ' -f 2)
|
terraform_version=$(terraform --version | head -n 1 | cut -d ' ' -f 2)
|
||||||
echo "${ZSH_THEME_TF_VERSION_PROMPT_PREFIX-[}${terraform_version:gs/%/%%}${ZSH_THEME_TF_VERSION_PROMPT_SUFFIX-]}"
|
echo "${ZSH_THEME_TF_VERSION_PROMPT_PREFIX-[}${terraform_version:gs/%/%%}${ZSH_THEME_TF_VERSION_PROMPT_SUFFIX-]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
alias tf='terraform'
|
alias tf='terraform'
|
||||||
alias tfa='terraform apply'
|
alias tfa='terraform apply'
|
||||||
alias tfa!='terraform apply -auto-approve'
|
alias tfa!='terraform apply -auto-approve'
|
||||||
alias tfap='terraform apply -parallelism=1'
|
alias tfap='terraform apply -parallelism=1'
|
||||||
|
alias tfapp='terraform apply tfplan'
|
||||||
alias tfc='terraform console'
|
alias tfc='terraform console'
|
||||||
alias tfd='terraform destroy'
|
alias tfd='terraform destroy'
|
||||||
alias tfd!='terraform destroy -auto-approve'
|
alias tfd!='terraform destroy -auto-approve'
|
||||||
@@ -31,6 +31,7 @@ alias tfiu='terraform init -upgrade'
|
|||||||
alias tfiur='terraform init -upgrade -reconfigure'
|
alias tfiur='terraform init -upgrade -reconfigure'
|
||||||
alias tfo='terraform output'
|
alias tfo='terraform output'
|
||||||
alias tfp='terraform plan'
|
alias tfp='terraform plan'
|
||||||
|
alias tfpo='terraform plan -out tfplan'
|
||||||
alias tfv='terraform validate'
|
alias tfv='terraform validate'
|
||||||
alias tfs='terraform state'
|
alias tfs='terraform state'
|
||||||
alias tft='terraform test'
|
alias tft='terraform test'
|
||||||
|
|||||||
Reference in New Issue
Block a user