From dbf4586594a30eeec3e3a39977a397d5ea4b6be0 Mon Sep 17 00:00:00 2001 From: Martin Wurm Date: Wed, 7 Feb 2024 17:46:41 +0100 Subject: [PATCH] Extend pass completion (#756) This PR extends the existing completion for the pass command to include all existing subcommands (as of pass v1.7.4). It also adds completions for the pass-otp and pass-update extensions. --- .../pass-otp/pass-otp-completions.nu | 41 ++++++ .../pass-update/pass-update-completions.nu | 17 +++ custom-completions/pass/nu-complete/mod.nu | 39 ++++++ custom-completions/pass/pass-completions.nu | 122 ++++++++++-------- 4 files changed, 163 insertions(+), 56 deletions(-) create mode 100644 custom-completions/pass/extensions/pass-otp/pass-otp-completions.nu create mode 100644 custom-completions/pass/extensions/pass-update/pass-update-completions.nu create mode 100644 custom-completions/pass/nu-complete/mod.nu diff --git a/custom-completions/pass/extensions/pass-otp/pass-otp-completions.nu b/custom-completions/pass/extensions/pass-otp/pass-otp-completions.nu new file mode 100644 index 00000000..ceb4297d --- /dev/null +++ b/custom-completions/pass/extensions/pass-otp/pass-otp-completions.nu @@ -0,0 +1,41 @@ +use ../../nu-complete "nu-complete pass-files" + +# Prompt for and insert a new OTP key. +export extern "pass otp insert" [ + name?: string@"nu-complete pass-files" # The name of the password entry to insert the OTP key into. If not provided, the URI label is used. + --force(-f) # Do not prompt before overwriting an existing URI. + --echo(-e) # Echo the input. + --secret(-s) # Prompt for the OTP secret, assuming SHA1 algorithm, 30-second period, and 6 OTP digits. If set, one of --issuer or --account is also required. If not set, prompt for a key URI. + --issuer(-i): string # The issuer of the OTP key. + --account(-a): string # The account the OTP key belongs to. +] + +# Appends an OTP key URI to an existing password file. +export extern "pass otp append" [ + name: string@"nu-complete pass-files" # The name of the password entry to insert the OTP key into. + --force(-f) # Do not prompt before overwriting an existing URI. + --echo(-e) # Echo the input. + --secret(-s) # Prompt for the OTP secret, assuming SHA1 algorithm, 30-second period, and 6 OTP digits. If set, one of --issuer or --account is also required. If not set, prompt for a key URI. + --issuer(-i): string # The issuer of the OTP key. + --account(-a): string # The account the OTP key belongs to. +] + +# Display the key URI stored in the given password entry. +export extern "pass otp uri" [ + name: string@"nu-complete pass-files" # The name of the password entry. + --clip(-c) # Put the URI on the clipboard. + --qrcode(-q) # Output the URI as a QR code. +] + +# Test if the given URI is a valid OTP key URI. +export extern "pass otp validate" [ + uri: string # The URI to validate. +] + +# Generate an OTP code. +export extern "pass otp" [ + name: string@"nu-complete pass-files" # The name of the password entry containing the OTP secret. + --clip(-c) # Put the OTP code on the clipboard and clear it after 45 seconds. +] + +export alias "pass otp code" = pass otp diff --git a/custom-completions/pass/extensions/pass-update/pass-update-completions.nu b/custom-completions/pass/extensions/pass-update/pass-update-completions.nu new file mode 100644 index 00000000..c4f4457c --- /dev/null +++ b/custom-completions/pass/extensions/pass-update/pass-update-completions.nu @@ -0,0 +1,17 @@ +use ../../nu-complete "nu-complete pass-files" + +# Interactively update a set of passwords. +export extern "pass update" [ + ...paths: string@"nu-complete pass-files" # The passwords and/or directories to update. + --clip(-c) # Write the password to the clipboard. + --no-symbols(-n) # Do not use any non-alphanumeric characters. + --length(-l): int = 25 # Provide a password length. + --provide(-p) # Let the user specify a password by hand. + --multiline(-m) # Update multiline passwords. If not set, only the first line of a password file is updated. + --include(-i): string # Only update the passwords that match a regex. + --exclude(-e): string # Do not update the passwords that match a regex. + --edit(-E) # Edit the passwords useing the default editor. + --force(-f) # Force update. + --version(-V) # Show version information. + --help(-h) # Print a help message. +] diff --git a/custom-completions/pass/nu-complete/mod.nu b/custom-completions/pass/nu-complete/mod.nu new file mode 100644 index 00000000..c26606ad --- /dev/null +++ b/custom-completions/pass/nu-complete/mod.nu @@ -0,0 +1,39 @@ +def pass_completions_directory [] { + if ($env | columns | any { |it| $it == "PASSWORD_STORE_DIR" }) { + return $env.PASSWORD_STORE_DIR + } else { + return ("~/.password-store" | path expand) + } +} + +export def "nu-complete pass-files" [] { + let dir = (pass_completions_directory) + ls ($dir | path join "**" | path join "*.gpg") + | get name + | each {|it| ( $it + | path relative-to $dir + | str replace ".gpg" "" + ) + } +} + +export def "nu-complete pass-directories" [] { + let dir = (pass_completions_directory) + ls ($dir | path join **) + | get name + | filter { |it| not (ls $it | is-empty) } + | each {|it| ( $it | path relative-to $dir) } +} + +export def "nu-complete pass-gpg" [] { + ^gpg --list-keys + | lines + | skip 2 + | split list '' + | each { |entry| + { + value: ($entry.1 | str trim), + description: ($entry.2 | parse --regex '^uid\s*\[[\w\s]*\]\s*(.*?)\s*$' | get 0.capture0) + } + } +} diff --git a/custom-completions/pass/pass-completions.nu b/custom-completions/pass/pass-completions.nu index c02a823a..187bf7f7 100644 --- a/custom-completions/pass/pass-completions.nu +++ b/custom-completions/pass/pass-completions.nu @@ -1,83 +1,93 @@ -def pass_completions_directory [] { - if ($env | columns | any { |it| $it == "PASSWORD_STORE_DIR" }) { - return $env.PASSWORD_STORE_DIR - } else { - return ("~/.password-store" | path expand) - } -} +use nu-complete * -def "nu-complete pass-files" [] { - let dir = (pass_completions_directory) - ls ($dir | path join "**" | path join "*.gpg") - | get name - | each {|it| ( $it - | path relative-to $dir - | str replace ".gpg" "" - ) - } -} +# Initialize new password storage or reencrypt existing passwords. +export extern "pass init" [ + ...gpg_ids: string@"nu-complete pass-gpg" # The ID(s) of the GPG public keys the passwords shall be encrypted with. + --path(-p): string@"nu-complete pass-directories" # Subfolder to selectively reencrypt. +] -def "nu-complete pass-directories" [] { - let dir = (pass_completions_directory) - ls ($dir | path join **) - | get name - | filter { |it| not (ls $it | is-empty) } - | each {|it| ( $it | path relative-to $dir) } -} - -# Show folders in the password store +# List passwords. export extern "pass ls" [ - subfolder?: string@"nu-complete pass-directories" + subfolder?: string@"nu-complete pass-directories" # The subfolder to list. ] -# Show the value of a password +# List passwords that match pass-names. +export extern "pass find" [ + ...pass_names: string # List of terms to search for. +] + +# Show the value of a password. export extern "pass show" [ - name: string@"nu-complete pass-files" - --clip(-c) # do not print the password but instead copy the first (or otherwise specified, example: -c2) line to the clipboard. - --qrcode(-q) # do not print the password but instead display a QR code. + name: string@"nu-complete pass-files" # The name of the password to show. + --clip(-c) # Do not print the password but instead copy the first (or otherwise specified, example: -c2) line to the clipboard. + --qrcode(-q) # Do not print the password but instead display a QR code. ] -# Add a new password -export extern "pass add" [ +# Search for password files containing a given search string when decrypted. +export extern "pass grep" [ + ...grepoptions # Options that will be passed to ^grep. + search_string: string # The search string. +] + +# Insert a new password +export extern "pass insert" [ name: string@"nu-complete pass-directories" - --echo(-e) # Enable keyboard echo - --multiline(-m) # Lines will be read until EOF or Ctrl+D is reached - --force(-f) # Omit prompt when trying to overwrite existing password + --echo(-e) # Enable keyboard echo. + --multiline(-m) # Lines will be read until EOF or Ctrl+D is reached. + --force(-f) # Omit prompt when trying to overwrite existing password. ] -# Edit an existing password +export alias "pass add" = pass insert + +# Edit an existing password. export extern "pass edit" [ - name: string@"nu-complete pass-files" + name: string@"nu-complete pass-files" # The name of the password. ] # Generate a new password export extern "pass generate" [ - name: string@"nu-complete pass-directories" - length?: number - --no-symbols(-n) # Do not use any non-alphanumeric characters in the generated password - --clip(-c) # Do not print the password but instead copy it to the clipboard using xclip or wl-clipboard - --in-place(-i) # Do not interactively prompt, and only replace the first line of the password file with the new generated password, keeping the remainder of the file intact - --force(-f) # Omit prompt when trying to overwrite existing password + name: string@"nu-complete pass-directories" # The name of the password. + length?: int = 25 # the length of the new password. + --no-symbols(-n) # Do not use any non-alphanumeric characters in the generated password. + --clip(-c) # Do not print the password but instead copy it to the clipboard using xclip or wl-clipboard. + --in-place(-i) # Do not interactively prompt, and only replace the first line of the password file with the new generated password, keeping the remainder of the file intact. + --force(-f) # Omit prompt when trying to overwrite existing password. ] -# Remove a password +# Remove a password or directory. export extern "pass rm" [ - name: string@"nu-complete pass-files" - --recursive(-r) # delete pass-name recursively if it is a directory - --force(-f) # Do not interactively prompt before removal + name: string@"nu-complete pass-files" # The name of the password or directory to remove. + --recursive(-r) # delete pass-name recursively if it is a directory. + --force(-f) # Do not interactively prompt before removal. ] -# Rename a password +# Rename or move a password or directory. export extern "pass mv" [ - oldname: string@"nu-complete pass-files" - newname: string@"nu-complete pass-directories" - --force(-f) # Omit prompt when trying to overwrite existing password + oldname: string@"nu-complete pass-files" # The password or directory to copy. + newname: string@"nu-complete pass-directories" # The path to copy the password or directory to. + --force(-f) # Omit prompt when trying to overwrite existing password. ] -# Copy a password +# Copy a password or directory. export extern "pass cp" [ - oldname: string@"nu-complete pass-files" - newname: string@"nu-complete pass-directories" - --force(-f) # Omit prompt when trying to overwrite existing password + oldname: string@"nu-complete pass-files" # The password or directory to copy. + newname: string@"nu-complete pass-directories" # The path to copy the password or directory to. + --force(-f) # Omit prompt when trying to overwrite existing password. ] +# If the password store is a git repository, execute a git command. +export extern "pass git" [ + ...git_command_args # The git command to run. +] + +# Show help information. +export extern "pass help" [] + +# Show version information. +export extern "pass version" [] + +export extern "pass" [ + path: string@"nu-complete pass-files" # The password to show or subfolder to list. + --clip(-c) # Do not print the password but instead copy the first (or otherwise specified, example: -c2) line to the clipboard. + --qrcode(-q) # Do not print the password but instead display a QR code. +]