From f30daa76efa3e35f269c5a5a993c82e94799a074 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Fri, 7 Aug 2020 14:35:58 +0000 Subject: aliases: Don't overshadow fd --- plugins/common-aliases/common-aliases.plugin.zsh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/common-aliases/common-aliases.plugin.zsh b/plugins/common-aliases/common-aliases.plugin.zsh index 023b1a51e..8f3045db4 100644 --- a/plugins/common-aliases/common-aliases.plugin.zsh +++ b/plugins/common-aliases/common-aliases.plugin.zsh @@ -34,7 +34,9 @@ alias -g P="2>&1| pygmentize -l pytb" alias dud='du -d 1 -h' alias duf='du -sh *' -alias fd='find . -type d -name' +if ! which fd >/dev/null 2>%1; then + alias fd='find . -type d -name' +fi alias ff='find . -type f -name' alias h='history' -- cgit v1.2.3-70-g09d2 From 7a76afd61e5daab6fb33f955930efa7f4cf16021 Mon Sep 17 00:00:00 2001 From: Eugéne Roux Date: Sun, 22 Nov 2020 16:42:39 +0200 Subject: style(ansible): minor style change (#8356) --- plugins/ansible/ansible.plugin.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ansible/ansible.plugin.zsh b/plugins/ansible/ansible.plugin.zsh index f68ff23a5..75393b704 100644 --- a/plugins/ansible/ansible.plugin.zsh +++ b/plugins/ansible/ansible.plugin.zsh @@ -4,7 +4,7 @@ function ansible-version(){ } function ansible-role-init(){ - if ! [ -z $1] ; then + if ! [ -z $1 ] ; then echo "Ansible Role : $1 Creating...." ansible-galaxy init $1 tree $1 @@ -25,4 +25,4 @@ alias ainv='ansible-inventory ' alias adoc='ansible-doc ' alias agal='ansible-galaxy ' alias apull='ansible-pull ' -alias aval='ansible-vault' \ No newline at end of file +alias aval='ansible-vault' -- cgit v1.2.3-70-g09d2 From d88887195fd5535ef2fd95180baa73af2a8c88f8 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Wed, 25 Nov 2020 20:14:28 +0100 Subject: fix(magic-enter): fix various bugs in the plugin (#9466) --- plugins/magic-enter/magic-enter.plugin.zsh | 53 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/plugins/magic-enter/magic-enter.plugin.zsh b/plugins/magic-enter/magic-enter.plugin.zsh index 2d4d578b6..55b893535 100644 --- a/plugins/magic-enter/magic-enter.plugin.zsh +++ b/plugins/magic-enter/magic-enter.plugin.zsh @@ -1,27 +1,38 @@ -# Bind quick stuff to enter! -# -# Pressing enter in a git directory runs `git status` -# in other directories `ls` -magic-enter () { - # If commands are not already set, use the defaults - [ -z "$MAGIC_ENTER_GIT_COMMAND" ] && MAGIC_ENTER_GIT_COMMAND="git status -u ." - [ -z "$MAGIC_ENTER_OTHER_COMMAND" ] && MAGIC_ENTER_OTHER_COMMAND="ls -lh ." +# Default commands +: ${MAGIC_ENTER_GIT_COMMAND:="git status -u ."} # run when in a git repository +: ${MAGIC_ENTER_OTHER_COMMAND:="ls -lh ."} # run anywhere else - if [[ -z $BUFFER ]]; then - echo "" - if git rev-parse --is-inside-work-tree &>/dev/null; then - eval "$MAGIC_ENTER_GIT_COMMAND" - else - eval "$MAGIC_ENTER_OTHER_COMMAND" - fi - zle redisplay +magic-enter() { + # Only run MAGIC_ENTER commands when in PS1 and command line is empty + # http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#User_002dDefined-Widgets + if [[ -n "$BUFFER" || "$CONTEXT" != start ]]; then + return + fi + + if command git rev-parse --is-inside-work-tree &>/dev/null; then + BUFFER="$MAGIC_ENTER_GIT_COMMAND" else - zle accept-line + BUFFER="$MAGIC_ENTER_OTHER_COMMAND" fi } -zle -N magic-enter +# Wrapper for the accept-line zle widget (run when pressing Enter) + +# If the wrapper already exists don't redefine it +(( ! ${+functions[_magic-enter_accept-line]} )) || return 0 + +case "$widgets[accept-line]" in + # Override the current accept-line widget, calling the old one + user:*) zle -N _magic-enter_orig_accept-line "${widgets[accept-line]#user:}" + function _magic-enter_accept-line() { + magic-enter + zle _magic-enter_orig_accept-line -- "$@" + } ;; + # If no user widget defined, call the original accept-line widget + builtin) function _magic-enter_accept-line() { + magic-enter + zle .accept-line + } ;; +esac -bindkey -M emacs "^M" magic-enter -bindkey -M vicmd "^M" magic-enter -bindkey -M viins "^M" magic-enter +zle -N accept-line _magic-enter_accept-line -- cgit v1.2.3-70-g09d2 From 05e2956dc61198d4767b96d97c5d10c93cedd6e3 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Thu, 26 Nov 2020 22:27:07 +0100 Subject: fix(git-auto-fetch): background `git-fetch-all` and other fixes (#9468) --- plugins/git-auto-fetch/README.md | 43 +++++++++---- plugins/git-auto-fetch/git-auto-fetch.plugin.zsh | 81 ++++++++++++++++-------- 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/plugins/git-auto-fetch/README.md b/plugins/git-auto-fetch/README.md index 35f3c2f71..e96ab42a3 100644 --- a/plugins/git-auto-fetch/README.md +++ b/plugins/git-auto-fetch/README.md @@ -1,26 +1,29 @@ # Git auto-fetch -Automatically fetches all changes from all remotes while you are working in git-initialized directory. +Automatically fetches all changes from all remotes while you are working in a git-initialized directory. -#### Usage - -Add `git-auto-fetch` to the plugins array in your zshrc file: +To use it, add `git-auto-fetch` to the plugins array in your zshrc file: ```shell plugins=(... git-auto-fetch) ``` -Every time you launch a command in your shell all remotes will be fetched in background. -By default autofetch will be triggered only if last fetch was done at least 60 seconds ago. -You can change fetch interval in your .zshrc: -``` -GIT_AUTO_FETCH_INTERVAL=1200 #in seconds +## Usage + +Every time the command prompt is shown all remotes will be fetched in the background. By default, +`git-auto-fetch` will be triggered only if the last auto-fetch was done at least 60 seconds ago. +You can change the fetch interval in your .zshrc: + +```sh +GIT_AUTO_FETCH_INTERVAL=1200 # in seconds ``` -Log of `git fetch --all` will be saved into `.git/FETCH_LOG` +A log of `git fetch --all` will be saved in `.git/FETCH_LOG`. + +## Toggle auto-fetch per folder -#### Toggle auto fetch per folder -If you are using mobile connection or for any other reason you can disable git-auto-fetch for any folder: +If you are using a mobile connection or for any other reason you can disable git-auto-fetch +for any folder: ```shell $ cd to/your/project @@ -29,3 +32,19 @@ disabled $ git-auto-fetch enabled ``` + +## Caveats + +Automatically fetching all changes defeats the purpose of `git push --force-with-lease`, +and makes it behave like `git push --force` in some cases. For example: + +Consider that you made some changes and possibly rebased some stuff, which means you'll +need to use `--force-with-lease` to overwrite the remote history of a branch. Between the +time when you make the changes (maybe do a `git log`) and the time when you `git push`, +it's possible that someone else updates the branch you're working on. + +If `git-auto-fetch` triggers then, you'll have fetched the remote changes without knowing +it, and even though you're running the push with `--force-with-lease`, git will overwrite +the recent changes because you already have them in your local repository. The +[`git push --force-with-lease` docs](https://git-scm.com/docs/git-push) talk about possible +solutions to this problem. diff --git a/plugins/git-auto-fetch/git-auto-fetch.plugin.zsh b/plugins/git-auto-fetch/git-auto-fetch.plugin.zsh index 5c42c21a7..0da84f2f5 100644 --- a/plugins/git-auto-fetch/git-auto-fetch.plugin.zsh +++ b/plugins/git-auto-fetch/git-auto-fetch.plugin.zsh @@ -1,36 +1,61 @@ -GIT_AUTO_FETCH_INTERVAL=${GIT_AUTO_FETCH_INTERVAL:=60} +# Default auto-fetch interval: 60 seconds +: ${GIT_AUTO_FETCH_INTERVAL:=60} + +# Necessary for the git-fetch-all function +zmodload zsh/datetime zsh/stat function git-fetch-all { - (`command git rev-parse --is-inside-work-tree 2>/dev/null` && - dir=`command git rev-parse --git-dir` && - [[ ! -f $dir/NO_AUTO_FETCH ]] && - (( `date +%s` - `date -r $dir/FETCH_LOG +%s 2>/dev/null || echo 0` > $GIT_AUTO_FETCH_INTERVAL )) && - GIT_SSH_COMMAND="command ssh -o BatchMode=yes" \ - command git fetch --all 2>/dev/null &>! $dir/FETCH_LOG &) + ( + # Get git root directory + if ! gitdir="$(command git rev-parse --git-dir 2>/dev/null)"; then + return 0 + fi + + # Do nothing if auto-fetch disabled + if [[ -z "$gitdir" || -f "$gitdir/NO_AUTO_FETCH" ]]; then + return 0 + fi + + # Get time (seconds) when auto-fetch was last run + lastrun="$(zstat +mtime "$gitdir/FETCH_LOG" 2>/dev/null || echo 0)" + # Do nothing if not enough time has passed since last auto-fetch + if (( EPOCHSECONDS - lastrun < $GIT_AUTO_FETCH_INTERVAL )); then + return 0 + fi + + # Fetch all remotes (avoid ssh passphrase prompt) + GIT_SSH_COMMAND="command ssh -o BatchMode=yes" \ + command git fetch --all 2>/dev/null &>! "$gitdir/FETCH_LOG" + ) &| } function git-auto-fetch { - `command git rev-parse --is-inside-work-tree 2>/dev/null` || return - guard="`command git rev-parse --git-dir`/NO_AUTO_FETCH" + # Do nothing if not in a git repository + command git rev-parse --is-inside-work-tree &>/dev/null || return 0 - (rm $guard 2>/dev/null && - echo "${fg_bold[green]}enabled${reset_color}") || - (touch $guard && - echo "${fg_bold[red]}disabled${reset_color}") + # Remove or create guard file depending on its existence + local guard="$(command git rev-parse --git-dir)/NO_AUTO_FETCH" + if [[ -f "$guard" ]]; then + command rm "$guard" && echo "${fg_bold[green]}enabled${reset_color}" + else + command touch "$guard" && echo "${fg_bold[red]}disabled${reset_color}" + fi } -# Override zle-line-init if it exists -if (( $+functions[zle-line-init] )); then - eval "override-git-auto-fetch-$(declare -f zle-line-init)" - - function zle-line-init () { - git-fetch-all - override-git-auto-fetch-zle-line-init - } -else - function zle-line-init () { - git-fetch-all - } -fi - -zle -N zle-line-init +# zle-line-init widget (don't redefine if already defined) +(( ! ${+functions[_git-auto-fetch_zle-line-init]} )) || return 0 + +case "$widgets[zle-line-init]" in + # Simply define the function if zle-line-init doesn't yet exist + builtin|"") function _git-auto-fetch_zle-line-init() { + git-fetch-all + } ;; + # Override the current zle-line-init widget, calling the old one + user:*) zle -N _git-auto-fetch_orig_zle-line-init "${widgets[zle-line-init]#user:}" + function _git-auto-fetch_zle-line-init() { + git-fetch-all + zle _git-auto-fetch_orig_zle-line-init -- "$@" + } ;; +esac + +zle -N zle-line-init _git-auto-fetch_zle-line-init -- cgit v1.2.3-70-g09d2 From 889cd7acf349f1cae81c07378986157b382f7f1d Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Sat, 31 Oct 2020 23:14:24 +0100 Subject: refactor(updater): switch to Zsh execution and fix git remote detection logic --- lib/cli.zsh | 2 +- lib/functions.zsh | 2 +- tools/check_for_upgrade.sh | 110 +++++++++++++++++++++++---------------------- tools/upgrade.sh | 98 ++++++++++++++++++++++------------------ 4 files changed, 112 insertions(+), 100 deletions(-) mode change 100644 => 100755 tools/upgrade.sh diff --git a/lib/cli.zsh b/lib/cli.zsh index b1478a89f..30790bec7 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -351,7 +351,7 @@ function _omz::theme::use { function _omz::update { # Run update script - env ZSH="$ZSH" sh "$ZSH/tools/upgrade.sh" + env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" # Update last updated file zmodload zsh/datetime echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" diff --git a/lib/functions.zsh b/lib/functions.zsh index 15526cd5f..f6f34e851 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -14,7 +14,7 @@ function upgrade_oh_my_zsh() { fi # Run update script - env ZSH="$ZSH" sh "$ZSH/tools/upgrade.sh" + env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" # Update last updated file zmodload zsh/datetime echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" diff --git a/tools/check_for_upgrade.sh b/tools/check_for_upgrade.sh index cadd5fe49..17925af8b 100644 --- a/tools/check_for_upgrade.sh +++ b/tools/check_for_upgrade.sh @@ -1,6 +1,6 @@ # Migrate .zsh-update file to $ZSH_CACHE_DIR if [[ -f ~/.zsh-update && ! -f "${ZSH_CACHE_DIR}/.zsh-update" ]]; then - mv ~/.zsh-update "${ZSH_CACHE_DIR}/.zsh-update" + mv ~/.zsh-update "${ZSH_CACHE_DIR}/.zsh-update" fi # Cancel update if: @@ -10,79 +10,81 @@ fi if [[ "$DISABLE_AUTO_UPDATE" = true ]] \ || [[ ! -w "$ZSH" || ! -O "$ZSH" ]] \ || ! command -v git &>/dev/null; then - return + return fi function current_epoch() { - zmodload zsh/datetime - echo $(( EPOCHSECONDS / 60 / 60 / 24 )) + zmodload zsh/datetime + echo $(( EPOCHSECONDS / 60 / 60 / 24 )) } function update_last_updated_file() { - echo "LAST_EPOCH=$(current_epoch)" >! "${ZSH_CACHE_DIR}/.zsh-update" + echo "LAST_EPOCH=$(current_epoch)" >! "${ZSH_CACHE_DIR}/.zsh-update" } function update_ohmyzsh() { - ZSH="$ZSH" sh "$ZSH/tools/upgrade.sh" - update_last_updated_file + ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + update_last_updated_file } () { - emulate -L zsh + emulate -L zsh - local epoch_target mtime option LAST_EPOCH + local epoch_target mtime option LAST_EPOCH - # Remove lock directory if older than a day - zmodload zsh/datetime - zmodload -F zsh/stat b:zstat - if mtime=$(zstat +mtime "$ZSH/log/update.lock" 2>/dev/null); then - if (( (mtime + 3600 * 24) < EPOCHSECONDS )); then - command rm -rf "$ZSH/log/update.lock" - fi + # Remove lock directory if older than a day + zmodload zsh/datetime + zmodload -F zsh/stat b:zstat + if mtime=$(zstat +mtime "$ZSH/log/update.lock" 2>/dev/null); then + if (( (mtime + 3600 * 24) < EPOCHSECONDS )); then + command rm -rf "$ZSH/log/update.lock" fi + fi - # Check for lock directory - if ! command mkdir "$ZSH/log/update.lock" 2>/dev/null; then - return - fi + # Check for lock directory + if ! command mkdir "$ZSH/log/update.lock" 2>/dev/null; then + return + fi - # Remove lock directory on exit. `return 1` is important for when trapping a SIGINT: - # The return status from the function is handled specially. If it is zero, the signal is - # assumed to have been handled, and execution continues normally. Otherwise, the shell - # will behave as interrupted except that the return status of the trap is retained. - trap "command rm -rf '$ZSH/log/update.lock'; return 1" EXIT INT QUIT + # Remove lock directory on exit. `return 1` is important for when trapping a SIGINT: + # The return status from the function is handled specially. If it is zero, the signal is + # assumed to have been handled, and execution continues normally. Otherwise, the shell + # will behave as interrupted except that the return status of the trap is retained. + trap " + unset -f current_epoch update_last_updated_file update_ohmyzsh + command rm -rf '$ZSH/log/update.lock' + return 1 + " EXIT INT QUIT - # Create or update .zsh-update file if missing or malformed - if ! source "${ZSH_CACHE_DIR}/.zsh-update" 2>/dev/null || [[ -z "$LAST_EPOCH" ]]; then - update_last_updated_file - return - fi + # Create or update .zsh-update file if missing or malformed + if ! source "${ZSH_CACHE_DIR}/.zsh-update" 2>/dev/null || [[ -z "$LAST_EPOCH" ]]; then + update_last_updated_file + return + fi - # Number of days before trying to update again - epoch_target=${UPDATE_ZSH_DAYS:-13} - # Test if enough time has passed until the next update - if (( ( $(current_epoch) - $LAST_EPOCH ) < $epoch_target )); then - return - fi + # Number of days before trying to update again + epoch_target=${UPDATE_ZSH_DAYS:-13} + # Test if enough time has passed until the next update + if (( ( $(current_epoch) - $LAST_EPOCH ) < $epoch_target )); then + return + fi - # Ask for confirmation before updating unless disabled - if [[ "$DISABLE_UPDATE_PROMPT" = true ]]; then - update_ohmyzsh - else - # input sink to swallow all characters typed before the prompt - # and add a newline if there wasn't one after characters typed - while read -t -k 1 option; do true; done - [[ "$option" != ($'\n'|"") ]] && echo + # Ask for confirmation before updating unless disabled + if [[ "$DISABLE_UPDATE_PROMPT" = true ]]; then + update_ohmyzsh + else + # input sink to swallow all characters typed before the prompt + # and add a newline if there wasn't one after characters typed + while read -t -k 1 option; do true; done + [[ "$option" != ($'\n'|"") ]] && echo - echo -n "[oh-my-zsh] Would you like to update? [Y/n] " - read -r -k 1 option - [[ "$option" != $'\n' ]] && echo - case "$option" in - [yY$'\n']) update_ohmyzsh ;; - [nN]) update_last_updated_file ;; - esac - fi + echo -n "[oh-my-zsh] Would you like to update? [Y/n] " + read -r -k 1 option + [[ "$option" != $'\n' ]] && echo + case "$option" in + [yY$'\n']) update_ohmyzsh ;; + [nN]) update_last_updated_file ;; + esac + fi } - -unset -f current_epoch update_last_updated_file update_ohmyzsh diff --git a/tools/upgrade.sh b/tools/upgrade.sh old mode 100644 new mode 100755 index e005519d6..634d5c03d --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -1,13 +1,23 @@ -# Use colors, but only if connected to a terminal, and that terminal -# supports them. +#!/usr/bin/env zsh + +cd "$ZSH" + +# Use colors, but only if connected to a terminal +# and that terminal supports them. + +local -a RAINBOW +local RED GREEN YELLOW BLUE UNDER BOLD RESET + if [ -t 1 ]; then - RB_RED=$(printf '\033[38;5;196m') - RB_ORANGE=$(printf '\033[38;5;202m') - RB_YELLOW=$(printf '\033[38;5;226m') - RB_GREEN=$(printf '\033[38;5;082m') - RB_BLUE=$(printf '\033[38;5;021m') - RB_INDIGO=$(printf '\033[38;5;093m') - RB_VIOLET=$(printf '\033[38;5;163m') + RAINBOW=( + "$(printf '\033[38;5;196m')" + "$(printf '\033[38;5;202m')" + "$(printf '\033[38;5;226m')" + "$(printf '\033[38;5;082m')" + "$(printf '\033[38;5;021m')" + "$(printf '\033[38;5;093m')" + "$(printf '\033[38;5;163m')" + ) RED=$(printf '\033[31m') GREEN=$(printf '\033[32m') @@ -16,25 +26,19 @@ if [ -t 1 ]; then BOLD=$(printf '\033[1m') UNDER=$(printf '\033[4m') RESET=$(printf '\033[m') -else - RB_RED="" - RB_ORANGE="" - RB_YELLOW="" - RB_GREEN="" - RB_BLUE="" - RB_INDIGO="" - RB_VIOLET="" - - RED="" - GREEN="" - YELLOW="" - BLUE="" - UNDER="" - BOLD="" - RESET="" fi -cd "$ZSH" +# Update upstream remote to ohmyzsh org +git remote -v | while read remote url _; do + case "$url" in + https://github.com/robbyrussell/oh-my-zsh(|.git)) + git remote set-url "$remote" "https://github.com/ohmyzsh/ohmyzsh.git" + break ;; + git@github.com:robbyrussell/oh-my-zsh(|.git)) + git remote set-url "$remote" "git@github.com:ohmyzsh/ohmyzsh.git" + break ;; + esac +done # Set git-config values known to fix git errors # Line endings (#4069) @@ -45,30 +49,36 @@ git config fsck.zeroPaddedFilemode ignore git config fetch.fsck.zeroPaddedFilemode ignore git config receive.fsck.zeroPaddedFilemode ignore # autostash on rebase (#7172) -resetAutoStash=$(git config --bool rebase.autoStash 2>&1) +resetAutoStash=$(git config --bool rebase.autoStash 2>/dev/null) git config rebase.autoStash true -# Update upstream remote to ohmyzsh org -remote=$(git remote -v | awk '/https:\/\/github\.com\/robbyrussell\/oh-my-zsh\.git/{ print $1; exit }') -if [ -n "$remote" ]; then - git remote set-url "$remote" "https://github.com/ohmyzsh/ohmyzsh.git" -fi +local ret=0 +# Update Oh My Zsh printf "${BLUE}%s${RESET}\n" "Updating Oh My Zsh" -if git pull --rebase --stat origin master -then - printf '%s %s__ %s %s %s %s %s__ %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf '%s ____ %s/ /_ %s ____ ___ %s__ __ %s ____ %s_____%s/ /_ %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf '%s / __ \%s/ __ \ %s / __ `__ \%s/ / / / %s /_ / %s/ ___/%s __ \ %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf '%s/ /_/ /%s / / / %s / / / / / /%s /_/ / %s / /_%s(__ )%s / / / %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf '%s\____/%s_/ /_/ %s /_/ /_/ /_/%s\__, / %s /___/%s____/%s_/ /_/ %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf '%s %s %s %s /____/ %s %s %s %s\n' $RB_RED $RB_ORANGE $RB_YELLOW $RB_GREEN $RB_BLUE $RB_INDIGO $RB_VIOLET $RB_RESET - printf "${BLUE}%s\n" "Hooray! Oh My Zsh has been updated and/or is at the current version." - printf "${BLUE}${BOLD}%s ${UNDER}%s${RESET}\n" "To keep up on the latest news and updates, follow us on Twitter:" "https://twitter.com/ohmyzsh" +last_commit=$(git rev-parse HEAD) +if git pull --rebase --stat origin master; then + # Check if it was really updated or not + if [[ "$(git rev-parse HEAD)" = "$last_commit" ]]; then + message="Oh My Zsh is already at the latest version." + ret=80 # non-zero exit code to indicate no changes pulled + else + message="Hooray! Oh My Zsh has been updated!" + fi + + printf '%s %s__ %s %s %s %s %s__ %s\n' $RAINBOW $RESET + printf '%s ____ %s/ /_ %s ____ ___ %s__ __ %s ____ %s_____%s/ /_ %s\n' $RAINBOW $RESET + printf '%s / __ \%s/ __ \ %s / __ `__ \%s/ / / / %s /_ / %s/ ___/%s __ \ %s\n' $RAINBOW $RESET + printf '%s/ /_/ /%s / / / %s / / / / / /%s /_/ / %s / /_%s(__ )%s / / / %s\n' $RAINBOW $RESET + printf '%s\____/%s_/ /_/ %s /_/ /_/ /_/%s\__, / %s /___/%s____/%s_/ /_/ %s\n' $RAINBOW $RESET + printf '%s %s %s %s /____/ %s %s %s %s\n' $RAINBOW $RESET + printf '\n' + printf "${BLUE}%s${RESET}\n" "$message" + printf "${BLUE}${BOLD}%s ${UNDER}%s${RESET}\n" "To keep up with the latest news and updates, follow us on Twitter:" "https://twitter.com/ohmyzsh" printf "${BLUE}${BOLD}%s ${UNDER}%s${RESET}\n" "Want to get involved in the community? Join our Discord:" "https://discord.gg/ohmyzsh" printf "${BLUE}${BOLD}%s ${UNDER}%s${RESET}\n" "Get your Oh My Zsh swag at:" "https://shop.planetargon.com/collections/oh-my-zsh" else - status=$? + ret=$? printf "${RED}%s${RESET}\n" 'There was an error updating. Try again later?' fi @@ -79,4 +89,4 @@ case "$resetAutoStash" in esac # Exit with `1` if the update failed -exit $status +exit $ret -- cgit v1.2.3-70-g09d2 From e093a4cf62d494dc46a8b68de1cb039c0bcf007a Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 2 Nov 2020 11:17:41 +0100 Subject: fix(updater): correctly restart the zsh session when the update pulled changes --- lib/cli.zsh | 488 +++++++++++++++++++++++++++--------------------------- lib/functions.zsh | 6 + 2 files changed, 253 insertions(+), 241 deletions(-) diff --git a/lib/cli.zsh b/lib/cli.zsh index 30790bec7..e8ce26131 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -1,22 +1,22 @@ #!/usr/bin/env zsh function omz { - [[ $# -gt 0 ]] || { - _omz::help - return 1 - } - - local command="$1" - shift - - # Subcommand functions start with _ so that they don't - # appear as completion entries when looking for `omz` - (( $+functions[_omz::$command] )) || { - _omz::help - return 1 - } - - _omz::$command "$@" + [[ $# -gt 0 ]] || { + _omz::help + return 1 + } + + local command="$1" + shift + + # Subcommand functions start with _ so that they don't + # appear as completion entries when looking for `omz` + (( $+functions[_omz::$command] )) || { + _omz::help + return 1 + } + + _omz::$command "$@" } function _omz { @@ -69,292 +69,298 @@ EOF } function _omz::confirm { - # If question supplied, ask it before reading the answer - # NOTE: uses the logname of the caller function - if [[ -n "$1" ]]; then - _omz::log prompt "$1" "${${functrace[1]#_}%:*}" - fi - - # Read one character - read -r -k 1 - - # If no newline entered, add a newline - if [[ "$REPLY" != $'\n' ]]; then - echo - fi + # If question supplied, ask it before reading the answer + # NOTE: uses the logname of the caller function + if [[ -n "$1" ]]; then + _omz::log prompt "$1" "${${functrace[1]#_}%:*}" + fi + + # Read one character + read -r -k 1 + + # If no newline entered, add a newline + if [[ "$REPLY" != $'\n' ]]; then + echo + fi } function _omz::log { - # if promptsubst is set, a message with `` or $() - # will be run even if quoted due to `print -P` - setopt localoptions nopromptsubst - - # $1 = info|warn|error|debug - # $2 = text - # $3 = (optional) name of the logger - - local logtype=$1 - local logname=${3:-${${functrace[1]#_}%:*}} - - # Don't print anything if debug is not active - if [[ $logtype = debug && -z $_OMZ_DEBUG ]]; then - return - fi - - # Choose coloring based on log type - case "$logtype" in - prompt) print -Pn "%S%F{blue}$logname%f%s: $2" ;; - debug) print -P "%F{white}$logname%f: $2" ;; - info) print -P "%F{green}$logname%f: $2" ;; - warn) print -P "%S%F{yellow}$logname%f%s: $2" ;; - error) print -P "%S%F{red}$logname%f%s: $2" ;; - esac >&2 + # if promptsubst is set, a message with `` or $() + # will be run even if quoted due to `print -P` + setopt localoptions nopromptsubst + + # $1 = info|warn|error|debug + # $2 = text + # $3 = (optional) name of the logger + + local logtype=$1 + local logname=${3:-${${functrace[1]#_}%:*}} + + # Don't print anything if debug is not active + if [[ $logtype = debug && -z $_OMZ_DEBUG ]]; then + return + fi + + # Choose coloring based on log type + case "$logtype" in + prompt) print -Pn "%S%F{blue}$logname%f%s: $2" ;; + debug) print -P "%F{white}$logname%f: $2" ;; + info) print -P "%F{green}$logname%f: $2" ;; + warn) print -P "%S%F{yellow}$logname%f%s: $2" ;; + error) print -P "%S%F{red}$logname%f%s: $2" ;; + esac >&2 } function _omz::plugin { - (( $# > 0 && $+functions[_omz::plugin::$1] )) || { - cat < 0 && $+functions[_omz::plugin::$1] )) || { + cat < [options] Available commands: - list List all available Oh My Zsh plugins + list List all available Oh My Zsh plugins EOF - return 1 - } + return 1 + } - local command="$1" - shift + local command="$1" + shift - _omz::plugin::$command "$@" + _omz::plugin::$command "$@" } function _omz::plugin::list { - local -a custom_plugins builtin_plugins - custom_plugins=("$ZSH_CUSTOM"/plugins/*(-/N:t)) - builtin_plugins=("$ZSH"/plugins/*(-/N:t)) - - # If the command is being piped, print all found line by line - if [[ ! -t 1 ]]; then - print -l ${(q-)custom_plugins} ${(q-)builtin_plugins} - return - fi - - if (( ${#custom_plugins} )); then - print -P "%U%BCustom plugins%b%u:" - print -l ${(q-)custom_plugins} | column - fi - - if (( ${#builtin_plugins} )); then - (( ${#custom_plugins} )) && echo # add a line of separation - - print -P "%U%BBuilt-in plugins%b%u:" - print -l ${(q-)builtin_plugins} | column - fi + local -a custom_plugins builtin_plugins + custom_plugins=("$ZSH_CUSTOM"/plugins/*(-/N:t)) + builtin_plugins=("$ZSH"/plugins/*(-/N:t)) + + # If the command is being piped, print all found line by line + if [[ ! -t 1 ]]; then + print -l ${(q-)custom_plugins} ${(q-)builtin_plugins} + return + fi + + if (( ${#custom_plugins} )); then + print -P "%U%BCustom plugins%b%u:" + print -l ${(q-)custom_plugins} | column + fi + + if (( ${#builtin_plugins} )); then + (( ${#custom_plugins} )) && echo # add a line of separation + + print -P "%U%BBuilt-in plugins%b%u:" + print -l ${(q-)builtin_plugins} | column + fi } function _omz::pr { - (( $# > 0 && $+functions[_omz::pr::$1] )) || { - cat < 0 && $+functions[_omz::pr::$1] )) || { + cat < [options] Available commands: - clean Delete all PR branches (ohmyzsh/pull-*) - test Fetch PR #NUMBER and rebase against master + clean Delete all PR branches (ohmyzsh/pull-*) + test Fetch PR #NUMBER and rebase against master EOF - return 1 - } + return 1 + } - local command="$1" - shift + local command="$1" + shift - _omz::pr::$command "$@" + _omz::pr::$command "$@" } function _omz::pr::clean { - ( - set -e - builtin cd -q "$ZSH" - - # Check if there are PR branches - local fmt branches - fmt="%(color:bold blue)%(align:18,right)%(refname:short)%(end)%(color:reset) %(color:dim bold red)%(objectname:short)%(color:reset) %(color:yellow)%(contents:subject)" - branches="$(command git for-each-ref --sort=-committerdate --color --format="$fmt" "refs/heads/ohmyzsh/pull-*")" - - # Exit if there are no PR branches - if [[ -z "$branches" ]]; then - _omz::log info "there are no Pull Request branches to remove." - return - fi - - # Print found PR branches - echo "$branches\n" - # Confirm before removing the branches - _omz::confirm "do you want remove these Pull Request branches? [Y/n] " - # Only proceed if the answer is a valid yes option - [[ "$REPLY" != [yY$'\n'] ]] && return - - _omz::log info "removing all Oh My Zsh Pull Request branches..." - command git branch --list 'ohmyzsh/pull-*' | while read branch; do - command git branch -D "$branch" - done - ) + ( + set -e + builtin cd -q "$ZSH" + + # Check if there are PR branches + local fmt branches + fmt="%(color:bold blue)%(align:18,right)%(refname:short)%(end)%(color:reset) %(color:dim bold red)%(objectname:short)%(color:reset) %(color:yellow)%(contents:subject)" + branches="$(command git for-each-ref --sort=-committerdate --color --format="$fmt" "refs/heads/ohmyzsh/pull-*")" + + # Exit if there are no PR branches + if [[ -z "$branches" ]]; then + _omz::log info "there are no Pull Request branches to remove." + return + fi + + # Print found PR branches + echo "$branches\n" + # Confirm before removing the branches + _omz::confirm "do you want remove these Pull Request branches? [Y/n] " + # Only proceed if the answer is a valid yes option + [[ "$REPLY" != [yY$'\n'] ]] && return + + _omz::log info "removing all Oh My Zsh Pull Request branches..." + command git branch --list 'ohmyzsh/pull-*' | while read branch; do + command git branch -D "$branch" + done + ) } function _omz::pr::test { - # Allow $1 to be a URL to the pull request - if [[ "$1" = https://* ]]; then - 1="${1:t}" - fi - - # Check the input - if ! [[ -n "$1" && "$1" =~ ^[[:digit:]]+$ ]]; then - echo >&2 "Usage: omz pr test " - return 1 - fi + # Allow $1 to be a URL to the pull request + if [[ "$1" = https://* ]]; then + 1="${1:t}" + fi + + # Check the input + if ! [[ -n "$1" && "$1" =~ ^[[:digit:]]+$ ]]; then + echo >&2 "Usage: omz pr test " + return 1 + fi + + # Save current git HEAD + local branch + branch=$(builtin cd -q "$ZSH"; git symbolic-ref --short HEAD) || { + _omz::log error "error when getting the current git branch. Aborting..." + return 1 + } + + + # Fetch PR onto ohmyzsh/pull- branch and rebase against master + # If any of these operations fail, undo the changes made + ( + set -e + builtin cd -q "$ZSH" + + # Get the ohmyzsh git remote + command git remote -v | while read remote url _; do + case "$url" in + https://github.com/ohmyzsh/ohmyzsh(|.git)) found=1; break ;; + git@github.com:ohmyzsh/ohmyzsh(|.git)) found=1; break ;; + esac + done + + (( $found )) || { + _omz::log error "could not found the ohmyzsh git remote. Aborting..." + return 1 + } - # Save current git HEAD - local branch - branch=$(builtin cd -q "$ZSH"; git symbolic-ref --short HEAD) || { - _omz::log error "error when getting the current git branch. Aborting..." - return 1 + # Fetch pull request head + _omz::log info "fetching PR #$1 to ohmyzsh/pull-$1..." + command git fetch -f "$remote" refs/pull/$1/head:ohmyzsh/pull-$1 || { + _omz::log error "error when trying to fetch PR #$1." + return 1 } + # Rebase pull request branch against the current master + _omz::log info "rebasing PR #$1..." + command git rebase master ohmyzsh/pull-$1 || { + command git rebase --abort &>/dev/null + _omz::log warn "could not rebase PR #$1 on top of master." + _omz::log warn "you might not see the latest stable changes." + _omz::log info "run \`zsh\` to test the changes." + return 1 + } - # Fetch PR onto ohmyzsh/pull- branch and rebase against master - # If any of these operations fail, undo the changes made - ( - set -e - builtin cd -q "$ZSH" - - # Get the ohmyzsh git remote - command git remote -v | while read remote url _; do - case "$url" in - https://github.com/ohmyzsh/ohmyzsh(|.git)) found=1; break ;; - git@github.com:ohmyzsh/ohmyzsh(|.git)) found=1; break ;; - esac - done - - (( $found )) || { - _omz::log error "could not found the ohmyzsh git remote. Aborting..." - return 1 - } - - # Fetch pull request head - _omz::log info "fetching PR #$1 to ohmyzsh/pull-$1..." - command git fetch -f "$remote" refs/pull/$1/head:ohmyzsh/pull-$1 || { - _omz::log error "error when trying to fetch PR #$1." - return 1 - } - - # Rebase pull request branch against the current master - _omz::log info "rebasing PR #$1..." - command git rebase master ohmyzsh/pull-$1 || { - command git rebase --abort &>/dev/null - _omz::log warn "could not rebase PR #$1 on top of master." - _omz::log warn "you might not see the latest stable changes." - _omz::log info "run \`zsh\` to test the changes." - return 1 - } - - _omz::log info "fetch of PR #${1} successful." - ) + _omz::log info "fetch of PR #${1} successful." + ) - # If there was an error, abort running zsh to test the PR - [[ $? -eq 0 ]] || return 1 + # If there was an error, abort running zsh to test the PR + [[ $? -eq 0 ]] || return 1 - # Run zsh to test the changes - _omz::log info "running \`zsh\` to test the changes. Run \`exit\` to go back." - command zsh -l + # Run zsh to test the changes + _omz::log info "running \`zsh\` to test the changes. Run \`exit\` to go back." + command zsh -l - # After testing, go back to the previous HEAD if the user wants - _omz::confirm "do you want to go back to the previous branch? [Y/n] " - # Only proceed if the answer is a valid yes option - [[ "$REPLY" != [yY$'\n'] ]] && return + # After testing, go back to the previous HEAD if the user wants + _omz::confirm "do you want to go back to the previous branch? [Y/n] " + # Only proceed if the answer is a valid yes option + [[ "$REPLY" != [yY$'\n'] ]] && return - ( - set -e - builtin cd -q "$ZSH" + ( + set -e + builtin cd -q "$ZSH" - command git checkout "$branch" -- || { - _omz::log error "could not go back to the previous branch ('$branch')." - return 1 - } - ) + command git checkout "$branch" -- || { + _omz::log error "could not go back to the previous branch ('$branch')." + return 1 + } + ) } function _omz::theme { - (( $# > 0 && $+functions[_omz::theme::$1] )) || { - cat < 0 && $+functions[_omz::theme::$1] )) || { + cat < [options] Available commands: - list List all available Oh My Zsh themes - use Load an Oh My Zsh theme + list List all available Oh My Zsh themes + use Load an Oh My Zsh theme EOF - return 1 - } + return 1 + } - local command="$1" - shift + local command="$1" + shift - _omz::theme::$command "$@" + _omz::theme::$command "$@" } function _omz::theme::list { - local -a custom_themes builtin_themes - custom_themes=("$ZSH_CUSTOM"/**/*.zsh-theme(.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::)) - builtin_themes=("$ZSH"/themes/*.zsh-theme(.N:t:r)) - - # If the command is being piped, print all found line by line - if [[ ! -t 1 ]]; then - print -l ${(q-)custom_themes} ${(q-)builtin_themes} - return - fi - - if (( ${#custom_themes} )); then - print -P "%U%BCustom themes%b%u:" - print -l ${(q-)custom_themes} | column - fi - - if (( ${#builtin_themes} )); then - (( ${#custom_themes} )) && echo # add a line of separation - - print -P "%U%BBuilt-in themes%b%u:" - print -l ${(q-)builtin_themes} | column - fi + local -a custom_themes builtin_themes + custom_themes=("$ZSH_CUSTOM"/**/*.zsh-theme(.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::)) + builtin_themes=("$ZSH"/themes/*.zsh-theme(.N:t:r)) + + # If the command is being piped, print all found line by line + if [[ ! -t 1 ]]; then + print -l ${(q-)custom_themes} ${(q-)builtin_themes} + return + fi + + if (( ${#custom_themes} )); then + print -P "%U%BCustom themes%b%u:" + print -l ${(q-)custom_themes} | column + fi + + if (( ${#builtin_themes} )); then + (( ${#custom_themes} )) && echo # add a line of separation + + print -P "%U%BBuilt-in themes%b%u:" + print -l ${(q-)builtin_themes} | column + fi } function _omz::theme::use { - if [[ -z "$1" ]]; then - echo >&2 "Usage: omz theme use " - return 1 - fi - - # Respect compatibility with old lookup order - if [[ -f "$ZSH_CUSTOM/$1.zsh-theme" ]]; then - source "$ZSH_CUSTOM/$1.zsh-theme" - elif [[ -f "$ZSH_CUSTOM/themes/$1.zsh-theme" ]]; then - source "$ZSH_CUSTOM/themes/$1.zsh-theme" - elif [[ -f "$ZSH/themes/$1.zsh-theme" ]]; then - source "$ZSH/themes/$1.zsh-theme" - else - _omz::log error "theme '$1' not found" - return 1 - fi + if [[ -z "$1" ]]; then + echo >&2 "Usage: omz theme use " + return 1 + fi + + # Respect compatibility with old lookup order + if [[ -f "$ZSH_CUSTOM/$1.zsh-theme" ]]; then + source "$ZSH_CUSTOM/$1.zsh-theme" + elif [[ -f "$ZSH_CUSTOM/themes/$1.zsh-theme" ]]; then + source "$ZSH_CUSTOM/themes/$1.zsh-theme" + elif [[ -f "$ZSH/themes/$1.zsh-theme" ]]; then + source "$ZSH/themes/$1.zsh-theme" + else + _omz::log error "theme '$1' not found" + return 1 + fi } function _omz::update { - # Run update script - env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" - # Update last updated file - zmodload zsh/datetime - echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" - # Remove update lock if it exists - command rm -rf "$ZSH/log/update.lock" + # Run update script + env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + local ret=$? + # Update last updated file + zmodload zsh/datetime + echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" + # Remove update lock if it exists + command rm -rf "$ZSH/log/update.lock" + # Restart the zsh session + if [[ $ret -eq 0 ]]; then + # Check whether to run a login shell + [[ "$ZSH_ARGZERO" = -* ]] && exec -l "${ZSH_ARGZERO#-}" || exec "$ZSH_ARGZERO" + fi } diff --git a/lib/functions.zsh b/lib/functions.zsh index f6f34e851..b0582b32b 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -15,11 +15,17 @@ function upgrade_oh_my_zsh() { # Run update script env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + local ret=$? # Update last updated file zmodload zsh/datetime echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" # Remove update lock if it exists command rm -rf "$ZSH/log/update.lock" + # Restart the zsh session + if [[ $ret -eq 0 ]]; then + # Check whether to run a login shell + [[ "$ZSH_ARGZERO" = -* ]] && exec -l "${ZSH_ARGZERO#-}" || exec "$ZSH_ARGZERO" + fi } function take() { -- cgit v1.2.3-70-g09d2 From eeab4e5186961f9af591d0264d96f0e39d154886 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Sun, 1 Nov 2020 00:26:03 +0100 Subject: feat(updater): add changelog display by parsing the commit list --- tools/changelog.sh | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/upgrade.sh | 7 + 2 files changed, 425 insertions(+) create mode 100755 tools/changelog.sh diff --git a/tools/changelog.sh b/tools/changelog.sh new file mode 100755 index 000000000..015b26c4b --- /dev/null +++ b/tools/changelog.sh @@ -0,0 +1,418 @@ +#!/usr/bin/env zsh + +############################## +# CHANGELOG SCRIPT CONSTANTS # +############################## + +#* Holds the list of valid types recognized in a commit subject +#* and the display string of such type +local -A TYPES +TYPES=( + [build]="Build system" + [chore]="Chore" + [ci]="CI" + [docs]="Documentation" + [feat]="Features" + [fix]="Bug fixes" + [perf]="Performance" + [refactor]="Refactor" + [style]="Style" + [test]="Testing" +) + +#* Types that will be displayed in their own section, +#* in the order specified here. +local -a MAIN_TYPES +MAIN_TYPES=(feat fix perf docs) + +#* Types that will be displayed under the category of other changes +local -a OTHER_TYPES +OTHER_TYPES=(refactor style other) + +#* Commit types that don't appear in $MAIN_TYPES nor $OTHER_TYPES +#* will not be displayed and will simply be ignored. + + +############################ +# COMMIT PARSING UTILITIES # +############################ + +function parse-commit { + + # This function uses the following globals as output: commits (A), + # subjects (A), scopes (A) and breaking (A). All associative arrays (A) + # have $hash as the key. + # - commits holds the commit type + # - subjects holds the commit subject + # - scopes holds the scope of a commit + # - breaking holds the breaking change warning if a commit does + # make a breaking change + + function commit:type { + local type="$(sed -E 's/^([a-zA-Z_\-]+)(\(.+\))?!?: .+$/\1/' <<< "$1")" + + # If $type doesn't appear in $TYPES array mark it as 'other' + if [[ -n "${(k)TYPES[(i)$type]}" ]]; then + echo $type + else + echo other + fi + } + + function commit:scope { + local scope + + # Try to find scope in "type():" format + scope=$(sed -nE 's/^[a-zA-Z_\-]+\((.+)\)!?: .+$/\1/p' <<< "$1") + if [[ -n "$scope" ]]; then + echo "$scope" + return + fi + + # If no scope found, try to find it in ":" format + # Make sure it's not a type before printing it + scope=$(sed -nE 's/^([a-zA-Z_\-]+): .+$/\1/p' <<< "$1") + if [[ -z "${(k)TYPES[(i)$scope]}" ]]; then + echo "$scope" + fi + } + + function commit:subject { + # Only display the relevant part of the commit, i.e. if it has the format + # type[(scope)!]: subject, where the part between [] is optional, only + # displays subject. If it doesn't match the format, returns the whole string. + sed -E 's/^[a-zA-Z_\-]+(\(.+\))?!?: (.+)$/\2/' <<< "$1" + } + + # Return subject if the body or subject match the breaking change format + function commit:is-breaking { + local subject="$1" body="$2" + + if [[ "$body" =~ "BREAKING CHANGE: (.*)" || \ + "$subject" =~ '^[^ :\)]+\)?!: (.*)$' ]]; then + echo "${match[1]}" + else + return 1 + fi + } + + # Return truncated hash of the reverted commit + function commit:is-revert { + local subject="$1" body="$2" + + if [[ "$subject" = Revert* && \ + "$body" =~ "This reverts commit ([^.]+)\." ]]; then + echo "${match[1]:0:7}" + else + return 1 + fi + } + + # Parse commit with hash $1 + local hash="$1" subject body warning rhash + subject="$(command git show -s --format=%s $hash)" + body="$(command git show -s --format=%b $hash)" + + # Commits following Conventional Commits (https://www.conventionalcommits.org/) + # have the following format, where parts between [] are optional: + # + # type[(scope)][!]: subject + # + # commit body + # [BREAKING CHANGE: warning] + + # commits holds the commit type + commits[$hash]="$(commit:type "$subject")" + # scopes holds the commit scope + scopes[$hash]="$(commit:scope "$subject")" + # subjects holds the commit subject + subjects[$hash]="$(commit:subject "$subject")" + + # breaking holds whether a commit has breaking changes + # and its warning message if it does + if warning=$(commit:is-breaking "$subject" "$body"); then + breaking[$hash]="$warning" + fi + + # reverts holds commits reverted in the same release + if rhash=$(commit:is-revert "$subject" "$body"); then + reverts[$hash]=$rhash + fi +} + +############################# +# RELEASE CHANGELOG DISPLAY # +############################# + +function display-release { + + # This function uses the following globals: output, version, + # commits (A), subjects (A), scopes (A), breaking (A) and reverts (A). + # + # - output is the output format to use when formatting (raw|text|md) + # - version is the version in which the commits are made + # - commits, subjects, scopes, breaking, and reverts are associative arrays + # with commit hashes as keys + + # Remove commits that were reverted + local hash rhash + for hash rhash in ${(kv)reverts}; do + if (( ${+commits[$rhash]} )); then + # Remove revert commit + unset "commits[$hash]" "subjects[$hash]" "scopes[$hash]" "breaking[$hash]" + # Remove reverted commit + unset "commits[$rhash]" "subjects[$rhash]" "scopes[$rhash]" "breaking[$rhash]" + fi + done + + # If no commits left skip displaying the release + if (( $#commits == 0 )); then + return + fi + + ##* Formatting functions + + # Format the hash according to output format + # If no parameter is passed, assume it comes from `$hash` + function fmt:hash { + #* Uses $hash from outer scope + local hash="${1:-$hash}" + case "$output" in + raw) printf "$hash" ;; + text) printf "\e[33m$hash\e[0m" ;; # red + md) printf "[\`$hash\`](https://github.com/ohmyzsh/ohmyzsh/commit/$hash)" ;; + esac + } + + # Format headers according to output format + # Levels 1 to 2 are considered special, the rest are formatted + # the same, except in md output format. + function fmt:header { + local header="$1" level="$2" + case "$output" in + raw) + case "$level" in + 1) printf "$header\n$(printf '%.0s=' {1..${#header}})\n\n" ;; + 2) printf "$header\n$(printf '%.0s-' {1..${#header}})\n\n" ;; + *) printf "$header:\n\n" ;; + esac ;; + text) + case "$level" in + 1|2) printf "\e[1;4m$header\e[0m\n\n" ;; # bold, underlined + *) printf "\e[1m$header:\e[0m\n\n" ;; # bold + esac ;; + md) printf "$(printf '%.0s#' {1..${level}}) $header\n\n" ;; + esac + } + + function fmt:scope { + #* Uses $scopes (A) and $hash from outer scope + local scope="${1:-${scopes[$hash]}}" + + # Get length of longest scope for padding + local max_scope=0 padding=0 + for hash in ${(k)scopes}; do + max_scope=$(( max_scope < ${#scopes[$hash]} ? ${#scopes[$hash]} : max_scope )) + done + + # If no scopes, exit the function + if [[ $max_scope -eq 0 ]]; then + return + fi + + # Get how much padding is required for this scope + padding=$(( max_scope < ${#scope} ? 0 : max_scope - ${#scope} )) + padding="${(r:$padding:: :):-}" + + # If no scope, print padding and 3 spaces (equivalent to "[] ") + if [[ -z "$scope" ]]; then + printf "${padding} " + return + fi + + # Print [scope] + case "$output" in + raw|md) printf "[$scope]${padding} " ;; + text) printf "[\e[38;5;9m$scope\e[0m]${padding} " ;; # red 9 + esac + } + + # If no parameter is passed, assume it comes from `$subjects[$hash]` + function fmt:subject { + #* Uses $subjects (A) and $hash from outer scope + local subject="${1:-${subjects[$hash]}}" + + # Capitalize first letter of the subject + subject="${(U)subject:0:1}${subject:1}" + + case "$output" in + raw) printf "$subject" ;; + # In text mode, highlight (#) and dim text between `backticks` + text) sed -E $'s|#([0-9]+)|\e[32m#\\1\e[0m|g;s|`(.+)`|`\e[2m\\1\e[0m`|g' <<< "$subject" ;; + # In markdown mode, link to (#) issues + md) sed -E 's|#([0-9]+)|[#\1](https://github.com/ohmyzsh/ohmyzsh/issues/\1)|g' <<< "$subject" ;; + esac + } + + function fmt:type { + #* Uses $type from outer scope + local type="${1:-${TYPES[$type]:-${(C)type}}}" + [[ -z "$type" ]] && return 0 + case "$output" in + raw|md) printf "$type: " ;; + text) printf "\e[4m$type\e[24m: " ;; # underlined + esac + } + + ##* Section functions + + function display:version { + fmt:header "$version" 2 + } + + function display:breaking { + (( $#breaking != 0 )) || return 0 + + case "$output" in + raw) display:type-header "BREAKING CHANGES" ;; + text|md) display:type-header "⚠ BREAKING CHANGES" ;; + esac + + local hash subject + for hash message in ${(kv)breaking}; do + echo " - $(fmt:hash) $(fmt:subject "${message}")" + done | sort + echo + } + + function display:type { + local hash type="$1" + + local -a hashes + hashes=(${(k)commits[(R)$type]}) + + # If no commits found of type $type, go to next type + (( $#hashes != 0 )) || return 0 + + fmt:header "${TYPES[$type]}" 3 + for hash in $hashes; do + echo " - $(fmt:hash) $(fmt:scope)$(fmt:subject)" + done | sort -k3 # sort by scope + echo + } + + function display:others { + local hash type + + # Commits made under types considered other changes + local -A changes + changes=(${(kv)commits[(R)${(j:|:)OTHER_TYPES}]}) + + # If no commits found under "other" types, don't display anything + (( $#changes != 0 )) || return 0 + + fmt:header "Other changes" 3 + for hash type in ${(kv)changes}; do + case "$type" in + other) echo " - $(fmt:hash) $(fmt:scope)$(fmt:subject)" ;; + *) echo " - $(fmt:hash) $(fmt:scope)$(fmt:type)$(fmt:subject)" ;; + esac + done | sort -k3 # sort by scope + echo + } + + ##* Release sections order + + # Display version header + display:version + + # Display breaking changes first + display:breaking + + # Display changes for commit types in the order specified + for type in $MAIN_TYPES; do + display:type "$type" + done + + # Display other changes + display:others +} + +function main { + # $1 = until commit, $2 = since commit + # $3 = output format (--raw|--text|--md) + local until="$1" since="$2" + local output=${${3:-"--text"}#--*} + + if [[ -z "$until" ]]; then + until=HEAD + fi + + # If $since is not specified, look up first version tag before $until + if [[ -z "$since" ]]; then + since=$(command git describe --abbrev=0 --tags "$until^" 2>/dev/null) || \ + unset since + elif [[ "$since" = --all ]]; then + unset since + fi + + # Commit classification arrays + local -A commits subjects scopes breaking reverts + local truncate=0 read_commits=0 + local hash version tag + + # Get the first version name: + # 1) try tag-like version, or + # 2) try name-rev, or + # 3) try branch name, or + # 4) try short hash + version=$(command git describe --tags $until 2>/dev/null) \ + || version=$(command git name-rev --no-undefined --name-only --exclude="remotes/*" $until 2>/dev/null) \ + || version=$(command git symbolic-ref --quiet --short $until 2>/dev/null) \ + || version=$(command git rev-parse --short $until 2>/dev/null) + + # Get commit list from $until commit until $since commit, or until root + # commit if $since is unset, in short hash form. + # --first-parent is used when dealing with merges: it only prints the + # merge commit, not the commits of the merged branch. + command git rev-list --first-parent --abbrev-commit --abbrev=7 ${since:+$since..}$until | while read hash; do + # Truncate list on versions with a lot of commits + if [[ -z "$since" ]] && (( ++read_commits > 35 )); then + truncate=1 + break + fi + + # If we find a new release (exact tag) + if tag=$(command git describe --exact-match --tags $hash 2>/dev/null); then + # Output previous release + display-release + # Reinitialize commit storage + commits=() + subjects=() + scopes=() + breaking=() + reverts=() + # Start work on next release + version="$tag" + read_commits=1 + fi + + parse-commit "$hash" + done + + display-release + + if (( truncate )); then + echo " ...more commits omitted" + echo + fi +} + +cd "$ZSH" + +# Use raw output if stdout is not a tty +if [[ ! -t 1 && -z "$3" ]]; then + main "$1" "$2" --raw +else + main "$@" +fi diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 634d5c03d..5d593b6c6 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -64,6 +64,13 @@ if git pull --rebase --stat origin master; then ret=80 # non-zero exit code to indicate no changes pulled else message="Hooray! Oh My Zsh has been updated!" + + # Display changelog with less if available, otherwise just print it to the terminal + if (( $+commands[less] )); then + command less -R <("$ZSH/tools/changelog.sh" HEAD "$last_commit") + else + "$ZSH/tools/changelog.sh" HEAD "$last_commit" + fi fi printf '%s %s__ %s %s %s %s %s__ %s\n' $RAINBOW $RESET -- cgit v1.2.3-70-g09d2 From 7712da89ee841f4773d0158370174200813169e1 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Wed, 4 Nov 2020 11:35:01 +0100 Subject: feat(CLI): add `omz changelog` command --- lib/cli.zsh | 109 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/lib/cli.zsh b/lib/cli.zsh index e8ce26131..253c554c1 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -20,53 +20,43 @@ function omz { } function _omz { - local -a cmds subcmds - cmds=( - 'help:Usage information' - 'plugin:Commands for Oh My Zsh plugins management' - 'pr:Commands for Oh My Zsh Pull Requests management' - 'theme:Commands for Oh My Zsh themes management' - 'update:Update Oh My Zsh' - ) - - if (( CURRENT == 2 )); then - _describe 'command' cmds - elif (( CURRENT == 3 )); then - case "$words[2]" in - plugin) subcmds=('list:List plugins') - _describe 'command' subcmds ;; - pr) subcmds=('test:Test a Pull Request' 'clean:Delete all Pull Request branches') - _describe 'command' subcmds ;; - theme) subcmds=('use:Load a theme' 'list:List themes') - _describe 'command' subcmds ;; - esac - elif (( CURRENT == 4 )); then - case "$words[2]::$words[3]" in - theme::use) compadd "$ZSH"/themes/*.zsh-theme(.N:t:r) \ - "$ZSH_CUSTOM"/**/*.zsh-theme(.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::) ;; - esac - fi + local -a cmds subcmds + cmds=( + 'changelog:Print the changelog' + 'help:Usage information' + 'plugin:Manage plugins' + 'pr:Manage Oh My Zsh Pull Requests' + 'theme:Manage themes' + 'update:Update Oh My Zsh' + ) - return 0 + if (( CURRENT == 2 )); then + _describe 'command' cmds + elif (( CURRENT == 3 )); then + case "$words[2]" in + changelog) local -a refs + refs=("${(@f)$(command git for-each-ref --format="%(refname:short):%(subject)" refs/heads refs/tags)}") + _describe 'command' refs ;; + plugin) subcmds=('list:List plugins') + _describe 'command' subcmds ;; + pr) subcmds=('test:Test a Pull Request' 'clean:Delete all Pull Request branches') + _describe 'command' subcmds ;; + theme) subcmds=('use:Load a theme' 'list:List themes') + _describe 'command' subcmds ;; + esac + elif (( CURRENT == 4 )); then + case "$words[2]::$words[3]" in + theme::use) compadd "$ZSH"/themes/*.zsh-theme(.N:t:r) \ + "$ZSH_CUSTOM"/**/*.zsh-theme(.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::) ;; + esac + fi + + return 0 } compdef _omz omz - -function _omz::help { - cat < [options] - -Available commands: - - help Print this help message - plugin Manage plugins - pr Manage Oh My Zsh Pull Requests - theme Manage themes - update Update Oh My Zsh - -EOF -} +## Utility functions function _omz::confirm { # If question supplied, ask it before reading the answer @@ -111,6 +101,41 @@ function _omz::log { esac >&2 } +## User-facing commands + +function _omz::help { + cat < [options] + +Available commands: + + help Print this help message + changelog Print the changelog + plugin Manage plugins + pr Manage Oh My Zsh Pull Requests + theme Manage themes + update Update Oh My Zsh + +EOF +} + +function _omz::changelog { + local version=${1:-HEAD} format=${3:-"--text"} + + if ! command git -C "$ZSH" show-ref --verify refs/heads/$version &>/dev/null && \ + ! command git -C "$ZSH" show-ref --verify refs/tags/$version &>/dev/null && \ + ! command git -C "$ZSH" rev-parse --verify "${version}^{commit}" &>/dev/null; then + cat < must be a valid branch, tag or commit. +EOF + return 1 + fi + + "$ZSH/tools/changelog.sh" "$version" "${2:-}" "$format" +} + function _omz::plugin { (( $# > 0 && $+functions[_omz::plugin::$1] )) || { cat < Date: Tue, 1 Dec 2020 11:12:31 +0100 Subject: fix(updater): make sure to run it with zsh --- tools/upgrade.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 5d593b6c6..e41817885 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -1,5 +1,9 @@ #!/usr/bin/env zsh +if [ -z "$ZSH_VERSION" ]; then + exec zsh "$0" +fi + cd "$ZSH" # Use colors, but only if connected to a terminal -- cgit v1.2.3-70-g09d2 From 3f8af040e9ed2d940451a5259e9c44308b695e60 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Wed, 2 Dec 2020 11:57:37 +0100 Subject: fix(updater): fix ignored variable name in read I used _ which is a convention in other languages, but in shell scripting $_ is a special variable set by the shell, and in Zsh versions older than 5.0.6 it complains for being a `read-only variable`. Fixes #9482 --- tools/upgrade.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/upgrade.sh b/tools/upgrade.sh index e41817885..2edf5b059 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -33,7 +33,7 @@ if [ -t 1 ]; then fi # Update upstream remote to ohmyzsh org -git remote -v | while read remote url _; do +git remote -v | while read remote url extra; do case "$url" in https://github.com/robbyrussell/oh-my-zsh(|.git)) git remote set-url "$remote" "https://github.com/ohmyzsh/ohmyzsh.git" -- cgit v1.2.3-70-g09d2 From 1ac40cd4456230f09bf0258b173304929d118992 Mon Sep 17 00:00:00 2001 From: Stimim Chen <106878+Stimim@users.noreply.github.com> Date: Thu, 3 Dec 2020 19:00:56 +0800 Subject: fix(git-prompt): make `gitstatus.py` python3-compatible (#9186) check_output() in get_tagname_or_hash() returns bytes instead of str in python3. Decode the return value to utf-8, this works in both python2 and python3. Co-authored-by: Stimim Chen --- plugins/git-prompt/gitstatus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/git-prompt/gitstatus.py b/plugins/git-prompt/gitstatus.py index 300365d71..bf3173614 100644 --- a/plugins/git-prompt/gitstatus.py +++ b/plugins/git-prompt/gitstatus.py @@ -11,11 +11,11 @@ def get_tagname_or_hash(): """return tagname if exists else hash""" # get hash hash_cmd = ['git', 'rev-parse', '--short', 'HEAD'] - hash_ = check_output(hash_cmd).strip() + hash_ = check_output(hash_cmd).decode('utf-8').strip() # get tagname tags_cmd = ['git', 'for-each-ref', '--points-at=HEAD', '--count=2', '--sort=-version:refname', '--format=%(refname:short)', 'refs/tags'] - tags = check_output(tags_cmd).split() + tags = check_output(tags_cmd).decode('utf-8').split() if tags: return tags[0] + ('+' if len(tags) > 1 else '') -- cgit v1.2.3-70-g09d2 From 81bbe86db0fd039c2b781b7466214ffaf19e3ca0 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Fri, 4 Dec 2020 12:33:36 +0100 Subject: fix(updater): properly show changelog via `less` --- tools/upgrade.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 2edf5b059..0e4c368bc 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -71,7 +71,7 @@ if git pull --rebase --stat origin master; then # Display changelog with less if available, otherwise just print it to the terminal if (( $+commands[less] )); then - command less -R <("$ZSH/tools/changelog.sh" HEAD "$last_commit") + "$ZSH/tools/changelog.sh" HEAD "$last_commit" --text | command less -R else "$ZSH/tools/changelog.sh" HEAD "$last_commit" fi -- cgit v1.2.3-70-g09d2 From 1aa2b18e293db8278a013322e42fd7aaa002a780 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Fri, 4 Dec 2020 12:34:01 +0100 Subject: feat(CLI): add `--unattended` flag to `omz update` to not reload zsh (#9187) --- lib/cli.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli.zsh b/lib/cli.zsh index 253c554c1..38cdc449c 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -384,7 +384,7 @@ function _omz::update { # Remove update lock if it exists command rm -rf "$ZSH/log/update.lock" # Restart the zsh session - if [[ $ret -eq 0 ]]; then + if [[ $ret -eq 0 && "$1" != --unattended ]]; then # Check whether to run a login shell [[ "$ZSH_ARGZERO" = -* ]] && exec -l "${ZSH_ARGZERO#-}" || exec "$ZSH_ARGZERO" fi -- cgit v1.2.3-70-g09d2 From 4f8964d8fff25b67e79029d570d171c8cdf3f833 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Fri, 4 Dec 2020 12:38:32 +0100 Subject: fix(changelog): fix highlight of codeblocks in subject --- tools/changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/changelog.sh b/tools/changelog.sh index 015b26c4b..55b86f923 100755 --- a/tools/changelog.sh +++ b/tools/changelog.sh @@ -248,7 +248,7 @@ function display-release { case "$output" in raw) printf "$subject" ;; # In text mode, highlight (#) and dim text between `backticks` - text) sed -E $'s|#([0-9]+)|\e[32m#\\1\e[0m|g;s|`(.+)`|`\e[2m\\1\e[0m`|g' <<< "$subject" ;; + text) sed -E $'s|#([0-9]+)|\e[32m#\\1\e[0m|g;s|`([^`]+)`|`\e[2m\\1\e[0m`|g' <<< "$subject" ;; # In markdown mode, link to (#) issues md) sed -E 's|#([0-9]+)|[#\1](https://github.com/ohmyzsh/ohmyzsh/issues/\1)|g' <<< "$subject" ;; esac -- cgit v1.2.3-70-g09d2 From fa1911f89eab6d8d753e302546c6ecd5d0db9e08 Mon Sep 17 00:00:00 2001 From: 赵崇延 <47052088+ZhaoChongyan@users.noreply.github.com> Date: Sat, 5 Dec 2020 22:42:45 +0800 Subject: fix(command-not-found): show error in Ubuntu when no package is found (#9418) --- .../command-not-found/command-not-found.plugin.zsh | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/command-not-found/command-not-found.plugin.zsh b/plugins/command-not-found/command-not-found.plugin.zsh index 81d76e638..aea1e1b4c 100644 --- a/plugins/command-not-found/command-not-found.plugin.zsh +++ b/plugins/command-not-found/command-not-found.plugin.zsh @@ -2,7 +2,22 @@ # as seen in https://www.porcheron.info/command-not-found-for-zsh/ # this is installed in Ubuntu -[[ -e /etc/zsh_command_not_found ]] && source /etc/zsh_command_not_found +if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then + function command_not_found_handler { + # check because c-n-f could've been removed in the meantime + if [ -x /usr/lib/command-not-found ]; then + /usr/lib/command-not-found -- "$1" + return $? + elif [ -x /usr/share/command-not-found/command-not-found ]; then + /usr/share/command-not-found/command-not-found -- "$1" + return $? + else + printf "zsh: command not found: %s\n" "$1" >&2 + return 127 + fi + return 0 + } +fi # Arch Linux command-not-found support, you must have package pkgfile installed # https://wiki.archlinux.org/index.php/Pkgfile#.22Command_not_found.22_hook @@ -10,13 +25,12 @@ # Fedora command-not-found support if [ -f /usr/libexec/pk-command-not-found ]; then - command_not_found_handler () { + command_not_found_handler() { runcnf=1 retval=127 [ ! -S /var/run/dbus/system_bus_socket ] && runcnf=0 [ ! -x /usr/libexec/packagekitd ] && runcnf=0 - if [ $runcnf -eq 1 ] - then + if [ $runcnf -eq 1 ]; then /usr/libexec/pk-command-not-found $@ retval=$? fi @@ -32,7 +46,7 @@ fi # NixOS command-not-found support if [ -x /run/current-system/sw/bin/command-not-found ]; then - command_not_found_handler () { + command_not_found_handler() { /run/current-system/sw/bin/command-not-found $@ } fi -- cgit v1.2.3-70-g09d2 From 5a888ff4ac58798ccdff0f32c4609fd153edc217 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 7 Dec 2020 19:53:07 +0100 Subject: fix(updater): don't show changelog when running unattended update (#9495) Fixes #9495 --- lib/cli.zsh | 8 +++++++- lib/functions.zsh | 2 +- tools/check_for_upgrade.sh | 2 +- tools/upgrade.sh | 10 ++++++---- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/cli.zsh b/lib/cli.zsh index 38cdc449c..92d447a8e 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -376,13 +376,19 @@ function _omz::theme::use { function _omz::update { # Run update script - env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + if [[ "$1" != --unattended ]]; then + ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" --interactive + else + ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + fi local ret=$? + # Update last updated file zmodload zsh/datetime echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" # Remove update lock if it exists command rm -rf "$ZSH/log/update.lock" + # Restart the zsh session if [[ $ret -eq 0 && "$1" != --unattended ]]; then # Check whether to run a login shell diff --git a/lib/functions.zsh b/lib/functions.zsh index b0582b32b..0fc758070 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -14,7 +14,7 @@ function upgrade_oh_my_zsh() { fi # Run update script - env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" --interactive local ret=$? # Update last updated file zmodload zsh/datetime diff --git a/tools/check_for_upgrade.sh b/tools/check_for_upgrade.sh index 17925af8b..d0ceba92d 100644 --- a/tools/check_for_upgrade.sh +++ b/tools/check_for_upgrade.sh @@ -24,7 +24,7 @@ function update_last_updated_file() { } function update_ohmyzsh() { - ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" + ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" --interactive update_last_updated_file } diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 0e4c368bc..cfd424527 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -70,10 +70,12 @@ if git pull --rebase --stat origin master; then message="Hooray! Oh My Zsh has been updated!" # Display changelog with less if available, otherwise just print it to the terminal - if (( $+commands[less] )); then - "$ZSH/tools/changelog.sh" HEAD "$last_commit" --text | command less -R - else - "$ZSH/tools/changelog.sh" HEAD "$last_commit" + if [[ "$1" = --interactive ]]; then + if (( $+commands[less] )); then + "$ZSH/tools/changelog.sh" HEAD "$last_commit" --text | LESS= command less -R + else + "$ZSH/tools/changelog.sh" HEAD "$last_commit" + fi fi fi -- cgit v1.2.3-70-g09d2 From 92fa8153d5e7e2e0d317f766f608e2062c76b390 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 7 Dec 2020 19:53:11 +0100 Subject: fix(changelog): fix assoc array syntax for zsh 5.4.2 and older (#9495) Also fixed a call to a defunct display:type-header function in displaying breaking changes. --- tools/changelog.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/changelog.sh b/tools/changelog.sh index 55b86f923..4ce42e5a3 100755 --- a/tools/changelog.sh +++ b/tools/changelog.sh @@ -8,16 +8,16 @@ #* and the display string of such type local -A TYPES TYPES=( - [build]="Build system" - [chore]="Chore" - [ci]="CI" - [docs]="Documentation" - [feat]="Features" - [fix]="Bug fixes" - [perf]="Performance" - [refactor]="Refactor" - [style]="Style" - [test]="Testing" + build "Build system" + chore "Chore" + ci "CI" + docs "Documentation" + feat "Features" + fix "Bug fixes" + perf "Performance" + refactor "Refactor" + style "Style" + test "Testing" ) #* Types that will be displayed in their own section, @@ -274,8 +274,8 @@ function display-release { (( $#breaking != 0 )) || return 0 case "$output" in - raw) display:type-header "BREAKING CHANGES" ;; - text|md) display:type-header "⚠ BREAKING CHANGES" ;; + raw) fmt:header "BREAKING CHANGES" 3 ;; + text|md) fmt:header "⚠ BREAKING CHANGES" 3 ;; esac local hash subject -- cgit v1.2.3-70-g09d2 From 4b7dba5b75cf5ed9e047d18c0a1588a52fa7f83b Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 7 Dec 2020 19:57:35 +0100 Subject: feat(lib)!: soft-deprecate `upgrade_oh_my_zsh` function for everybody BREAKING CHANGE: the `upgrade_oh_my_zsh` function now calls `omz update` directly. --- lib/functions.zsh | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/functions.zsh b/lib/functions.zsh index 0fc758070..7beb62ad9 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -9,23 +9,8 @@ function uninstall_oh_my_zsh() { } function upgrade_oh_my_zsh() { - if (( $+functions[_omz::update] )); then - echo >&2 "${fg[yellow]}Note: \`$0\` is deprecated. Use \`omz update\` instead.$reset_color" - fi - - # Run update script - env ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" --interactive - local ret=$? - # Update last updated file - zmodload zsh/datetime - echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update" - # Remove update lock if it exists - command rm -rf "$ZSH/log/update.lock" - # Restart the zsh session - if [[ $ret -eq 0 ]]; then - # Check whether to run a login shell - [[ "$ZSH_ARGZERO" = -* ]] && exec -l "${ZSH_ARGZERO#-}" || exec "$ZSH_ARGZERO" - fi + echo >&2 "${fg[yellow]}Note: \`$0\` is deprecated. Use \`omz update\` instead.$reset_color" + omz update } function take() { -- cgit v1.2.3-70-g09d2 From e48be7dfc8dfb1e7110bdecc8c9dfcb9380953dd Mon Sep 17 00:00:00 2001 From: Veesar <74916913+veesar@users.noreply.github.com> Date: Tue, 8 Dec 2020 01:53:55 +0530 Subject: feat(wp-cli): add aliases for DB management (#9469) --- plugins/wp-cli/README.md | 202 ++++++++++++++++++++------------------- plugins/wp-cli/wp-cli.plugin.zsh | 51 ++-------- 2 files changed, 109 insertions(+), 144 deletions(-) diff --git a/plugins/wp-cli/README.md b/plugins/wp-cli/README.md index 43c41eb53..c4993ab4c 100644 --- a/plugins/wp-cli/README.md +++ b/plugins/wp-cli/README.md @@ -1,107 +1,109 @@ # WP-CLI -**Maintainer:** [joshmedeski](https://github.com/joshmedeski) - -WordPress Command Line Interface (https://wp-cli.org/) - -WP-CLI is a set of command-line tools for managing WordPress installations. You can update plugins, set up multisite installs and much more, without using a web browser. - -This plugin adds [tab completion](https://wp-cli.org/#tab-completions) for `wp-cli` as well as several aliases. - -## List of Aliases - -### Core -- wpcc='wp core config' -- wpcd='wp core download' -- wpci='wp core install' -- wpcii='wp core is-installed' -- wpcmc='wp core multisite-convert' -- wpcmi='wp core multisite-install' -- wpcu='wp core update' -- wpcudb='wp core update-db' -- wpcvc='wp core verify-checksums' - -### Cron -- wpcre='wp cron event' -- wpcrs='wp cron schedule' -- wpcrt='wp cron test' +The [WordPress CLI](https://wp-cli.org/) is a command-line tool for managing WordPress installations. You can update plugins, set up multisite installs and much more, without using a web browser. -### Menu -- wpmc='wp menu create' -- wpmd='wp menu delete' -- wpmi='wp menu item' -- wpml='wp menu list' -- wpmlo='wp menu location' +This plugin adds [tab completion](https://wp-cli.org/#tab-completions) for `wp-cli` as well as several aliases for commonly used commands. -### Plugin -- wppa='activate' -- wppda='deactivate' -- wppd='delete' -- wppg='get' -- wppi='install' -- wppis='is-installed' -- wppl='list' -- wppp='path' -- wpps='search' -- wppst='status' -- wppt='toggle' -- wppun='uninstall' -- wppu='update' +To use it, add `wp-cli` to the plugins array in your zshrc file: -### Post -- wppoc='wp post create' -- wppod='wp post delete' -- wppoe='wp post edit' -- wppogen='wp post generate' -- wppog='wp post get' -- wppol='wp post list' -- wppom='wp post meta' -- wppou='wp post update' -- wppourl='wp post url' +```zsh +plugins=(... wp-cli) +``` -### Sidebar -- wpsbl='wp sidebar list' - -### Theme -- wpta='wp theme activate' -- wptd='wp theme delete' -- wptdis='wp theme disable' -- wpte='wp theme enable' -- wptg='wp theme get' -- wpti='wp theme install' -- wptis='wp theme is-installed' -- wptl='wp theme list' -- wptm='wp theme mod' -- wptp='wp theme path' -- wpts='wp theme search' -- wptst='wp theme status' -- wptu='wp theme update' - -### User -- wpuac='wp user add-cap' -- wpuar='wp user add-role' -- wpuc='wp user create' -- wpud='wp user delete' -- wpugen='wp user generate' -- wpug='wp user get' -- wpui='wp user import-csv' -- wpul='wp user list' -- wpulc='wp user list-caps' -- wpum='wp user meta' -- wpurc='wp user remove-cap' -- wpurr='wp user remove-role' -- wpusr='wp user set-role' -- wpuu='wp user update' - -### Widget -- wpwa='wp widget add' -- wpwda='wp widget deactivate' -- wpwd='wp widget delete' -- wpwl='wp widget list' -- wpwm='wp widget move' -- wpwu='wp widget update' - -The entire list of wp-cli commands can be found here: https://wp-cli.org/commands/ - -I only included the commands that are most used. Please feel free to contribute to this project if you want more commands. +**Maintainer:** [joshmedeski](https://github.com/joshmedeski) +## Aliases + +The entire list of `wp-cli` commands can be found here: https://developer.wordpress.org/cli/commands/ + +| Alias | Command | +|-----------|-----------------------------| +| **Core** | +| `wpcc` | `wp core config` | +| `wpcd` | `wp core download` | +| `wpci` | `wp core install` | +| `wpcii` | `wp core is-installed` | +| `wpcmc` | `wp core multisite-convert` | +| `wpcmi` | `wp core multisite-install` | +| `wpcu` | `wp core update` | +| `wpcudb` | `wp core update-db` | +| `wpcvc` | `wp core verify-checksums` | +| **Cron** | +| `wpcre` | `wp cron event` | +| `wpcrs` | `wp cron schedule` | +| `wpcrt` | `wp cron test` | +| **Database** | +| `wpdbe` | `wp db export` | +| `wpdbi` | `wp db import` | +| `wpdbcr` | `wp db create` | +| `wpdbs` | `wp db search` | +| `wpdbch` | `wp db check` | +| `wpdbr` | `wp db repair` | +| **Menu** | +| `wpmc` | `wp menu create` | +| `wpmd` | `wp menu delete` | +| `wpmi` | `wp menu item` | +| `wpml` | `wp menu list` | +| `wpmlo` | `wp menu location` | +| **Plugin** | +| `wppa` | `wp plugin activate` | +| `wppda` | `wp plugin deactivate` | +| `wppd` | `wp plugin delete` | +| `wppg` | `wp plugin get` | +| `wppi` | `wp plugin install` | +| `wppis` | `wp plugin is-installed` | +| `wppl` | `wp plugin list` | +| `wppp` | `wp plugin path` | +| `wpps` | `wp plugin search` | +| `wppst` | `wp plugin status` | +| `wppt` | `wp plugin toggle` | +| `wppun` | `wp plugin uninstall` | +| `wppu` | `wp plugin update` | +| **Post** | +| `wppoc` | `wp post create` | +| `wppod` | `wp post delete` | +| `wppoe` | `wp post edit` | +| `wppogen` | `wp post generate` | +| `wppog` | `wp post get` | +| `wppol` | `wp post list` | +| `wppom` | `wp post meta` | +| `wppou` | `wp post update` | +| `wppourl` | `wp post url` | +| **Sidebar** | +| `wpsbl` | `wp sidebar list` | +| **Theme** | +| `wpta` | `wp theme activate` | +| `wptd` | `wp theme delete` | +| `wptdis` | `wp theme disable` | +| `wpte` | `wp theme enable` | +| `wptg` | `wp theme get` | +| `wpti` | `wp theme install` | +| `wptis` | `wp theme is-installed` | +| `wptl` | `wp theme list` | +| `wptm` | `wp theme mod` | +| `wptp` | `wp theme path` | +| `wpts` | `wp theme search` | +| `wptst` | `wp theme status` | +| `wptu` | `wp theme update` | +| **User** | +| `wpuac` | `wp user add-cap` | +| `wpuar` | `wp user add-role` | +| `wpuc` | `wp user create` | +| `wpud` | `wp user delete` | +| `wpugen` | `wp user generate` | +| `wpug` | `wp user get` | +| `wpui` | `wp user import-csv` | +| `wpul` | `wp user list` | +| `wpulc` | `wp user list-caps` | +| `wpum` | `wp user meta` | +| `wpurc` | `wp user remove-cap` | +| `wpurr` | `wp user remove-role` | +| `wpusr` | `wp user set-role` | +| `wpuu` | `wp user update` | +| **Widget** | +| `wpwa` | `wp widget add` | +| `wpwda` | `wp widget deactivate` | +| `wpwd` | `wp widget delete` | +| `wpwl` | `wp widget list` | +| `wpwm` | `wp widget move` | +| `wpwu` | `wp widget update` | diff --git a/plugins/wp-cli/wp-cli.plugin.zsh b/plugins/wp-cli/wp-cli.plugin.zsh index 97bed406e..09bdf3260 100644 --- a/plugins/wp-cli/wp-cli.plugin.zsh +++ b/plugins/wp-cli/wp-cli.plugin.zsh @@ -2,14 +2,6 @@ # A command line interface for WordPress # https://wp-cli.org/ -# Cache - -# Cap - -# CLI - -# Comment - # Core alias wpcc='wp core config' alias wpcd='wp core download' @@ -27,18 +19,12 @@ alias wpcrs='wp cron schedule' alias wpcrt='wp cron test' # Db - -# Eval - -# Eval-File - -# Export - -# Help - -# Import - -# Media +alias wpdbe='wp db export' +alias wpdbi='wp db import' +alias wpdbcr='wp db create' +alias wpdbs='wp db search' +alias wpdbch='wp db check' +alias wpdbr='wp db repair' # Menu alias wpmc='wp menu create' @@ -47,10 +33,6 @@ alias wpmi='wp menu item' alias wpml='wp menu list' alias wpmlo='wp menu location' -# Network - -# Option - # Plugin alias wppa='wp plugin activate' alias wppda='wp plugin deactivate' @@ -77,25 +59,9 @@ alias wppom='wp post meta' alias wppou='wp post update' alias wppourl='wp post url' -# Rewrite - -# Role - -# Scaffold - -# Search-Replace - -# Shell - # Sidebar alias wpsbl='wp sidebar list' -# Site - -# Super-Admin - -# Term - # Theme alias wpta='wp theme activate' alias wptd='wp theme delete' @@ -111,8 +77,6 @@ alias wpts='wp theme search' alias wptst='wp theme status' alias wptu='wp theme update' -# Transient - # User alias wpuac='wp user add-cap' alias wpuar='wp user add-role' @@ -138,9 +102,8 @@ alias wpwm='wp widget move' alias wpwu='wp widget update' +# Completion for wp autoload -U +X bashcompinit && bashcompinit -# bash completion for the `wp` command - _wp_complete() { local cur=${COMP_WORDS[COMP_CWORD]} -- cgit v1.2.3-70-g09d2 From 865d2d8a53f65eba22c8a0436be1ea985aaa2aba Mon Sep 17 00:00:00 2001 From: ratijas Date: Tue, 8 Dec 2020 00:00:25 +0300 Subject: refactor(archlinux): prevent leaking local variables (#9476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marc Cornellà --- plugins/archlinux/archlinux.plugin.zsh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/plugins/archlinux/archlinux.plugin.zsh b/plugins/archlinux/archlinux.plugin.zsh index 4d39bd86b..c72e06dc2 100644 --- a/plugins/archlinux/archlinux.plugin.zsh +++ b/plugins/archlinux/archlinux.plugin.zsh @@ -171,14 +171,13 @@ function paclist() { } function pacdisowned() { - emulate -L zsh - + local tmp db fs tmp=${TMPDIR-/tmp}/pacman-disowned-$UID-$$ db=$tmp/db fs=$tmp/fs mkdir "$tmp" - trap 'rm -rf "$tmp"' EXIT + trap 'rm -rf "$tmp"' EXIT pacman -Qlq | sort -u > "$db" @@ -189,15 +188,14 @@ function pacdisowned() { } function pacmanallkeys() { - emulate -L zsh curl -s https://www.archlinux.org/people/{developers,trustedusers}/ | \ awk -F\" '(/pgp.mit.edu/) { sub(/.*search=0x/,""); print $1}' | \ xargs sudo pacman-key --recv-keys } function pacmansignkeys() { - emulate -L zsh - for key in $*; do + local key + for key in $@; do sudo pacman-key --recv-keys $key sudo pacman-key --lsign-key $key printf 'trust\n3\n' | sudo gpg --homedir /etc/pacman.d/gnupg \ @@ -207,13 +205,13 @@ function pacmansignkeys() { if (( $+commands[xdg-open] )); then function pacweb() { - pkg="$1" - infos="$(LANG=C pacman -Si "$pkg")" + local pkg="$1" + local infos="$(LANG=C pacman -Si "$pkg")" if [[ -z "$infos" ]]; then return fi - repo="$(grep -m 1 '^Repo' <<< "$infos" | grep -oP '[^ ]+$')" - arch="$(grep -m 1 '^Arch' <<< "$infos" | grep -oP '[^ ]+$')" + local repo="$(grep -m 1 '^Repo' <<< "$infos" | grep -oP '[^ ]+$')" + local arch="$(grep -m 1 '^Arch' <<< "$infos" | grep -oP '[^ ]+$')" xdg-open "https://www.archlinux.org/packages/$repo/$arch/$pkg/" &>/dev/null } fi -- cgit v1.2.3-70-g09d2 From 452586e79a06d568a3445b26e6d6f4d62c111f52 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 7 Dec 2020 21:28:08 +0100 Subject: chore: add Josh Medeski as `wp-cli` plugin maintainer --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e16ebb39b..8e175d549 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,3 +3,4 @@ plugins/aws/ @maksyms plugins/git-lfs/ @vietduc01100001 plugins/gitfast/ @felipec plugins/sdk/ @rgoldberg +plugins/wp-cli/ @joshmedeski -- cgit v1.2.3-70-g09d2 From e2f2489a666caf238804783b4e8851a781ac6ffc Mon Sep 17 00:00:00 2001 From: Joseph Heyburn <34041368+jdheyburn@users.noreply.github.com> Date: Tue, 8 Dec 2020 17:25:42 +0000 Subject: fix(git): silently fail in `git_main_branch` if not in a git repo (#9484) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marc Cornellà --- plugins/git/git.plugin.zsh | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/git/git.plugin.zsh b/plugins/git/git.plugin.zsh index bf2619976..e32136f15 100644 --- a/plugins/git/git.plugin.zsh +++ b/plugins/git/git.plugin.zsh @@ -31,6 +31,7 @@ function work_in_progress() { # Check if main exists and use instead of master function git_main_branch() { + command git rev-parse --git-dir &>/dev/null || return local branch for branch in main trunk; do if command git show-ref -q --verify refs/heads/$branch; then -- cgit v1.2.3-70-g09d2 From e5af22cca6a07c9be52fe48810faaa08261e4d15 Mon Sep 17 00:00:00 2001 From: Mark Mercado Date: Wed, 9 Dec 2020 11:59:12 -0500 Subject: feat(plugins): add grc plugin for Generic Colouriser (#9315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marc Cornellà --- plugins/grc/README.md | 37 +++++++++++++++++++++++++++++++++++++ plugins/grc/grc.plugin.zsh | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 plugins/grc/README.md create mode 100644 plugins/grc/grc.plugin.zsh diff --git a/plugins/grc/README.md b/plugins/grc/README.md new file mode 100644 index 000000000..dfda41466 --- /dev/null +++ b/plugins/grc/README.md @@ -0,0 +1,37 @@ +# Generic Colouriser plugin + +This plugin adds wrappers for commands supported by [Generic Colouriser](https://github.com/garabik/grc): + +To use it, add `grc` to the plugins array in your zshrc file: + +```zsh +plugins=(... grc) +``` + +## Commands + +The following commands are wrapped by `grc` so that their output is automatically colored: + +- `cc` +- `configure` +- `cvs` +- `df` +- `diff` +- `dig` +- `gcc` +- `gmake` +- `ifconfig` +- `iwconfig` +- `last` +- `ldap` +- `make` +- `mount` +- `mtr` +- `netstat` +- `ping` +- `ping6` +- `ps` +- `traceroute` +- `traceroute6` +- `wdiff` +- `whois` diff --git a/plugins/grc/grc.plugin.zsh b/plugins/grc/grc.plugin.zsh new file mode 100644 index 000000000..6a52ec568 --- /dev/null +++ b/plugins/grc/grc.plugin.zsh @@ -0,0 +1,44 @@ +# Adapted from: https://github.com/garabik/grc/blob/master/grc.zsh + +if [[ "$TERM" = dumb ]] || (( ! $+commands[grc] )); then + return +fi + +# Supported commands +cmds=( + cc + configure + cvs + df + diff + dig + gcc + gmake + ifconfig + iwconfig + last + ldap + make + mount + mtr + netstat + ping + ping6 + ps + traceroute + traceroute6 + wdiff + whois +) + +# Set alias for supported commands +for cmd in $cmds; do + if (( $+commands[$cmd] )); then + eval "function $cmd { + grc --colour=auto \"${commands[$cmd]}\" \"\$@\" + }" + fi +done + +# Clean up variables +unset cmds cmd -- cgit v1.2.3-70-g09d2 From fbee1ff8bda5a4491a4d003023f27daf04e09f4a Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Thu, 10 Dec 2020 01:50:01 +0530 Subject: fd: Rework command to be more idiomatic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marc Cornellà --- plugins/common-aliases/common-aliases.plugin.zsh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/common-aliases/common-aliases.plugin.zsh b/plugins/common-aliases/common-aliases.plugin.zsh index 8f3045db4..231037a92 100644 --- a/plugins/common-aliases/common-aliases.plugin.zsh +++ b/plugins/common-aliases/common-aliases.plugin.zsh @@ -34,9 +34,7 @@ alias -g P="2>&1| pygmentize -l pytb" alias dud='du -d 1 -h' alias duf='du -sh *' -if ! which fd >/dev/null 2>%1; then - alias fd='find . -type d -name' -fi +(( $+commands[fd] )) || alias fd='find . -type d -name' alias ff='find . -type f -name' alias h='history' -- cgit v1.2.3-70-g09d2 From 5feb2b67428546c7e50b510f9d644ce24e1d5c43 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Wed, 9 Dec 2020 23:11:31 +0100 Subject: Document fd alias change in README --- plugins/common-aliases/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/common-aliases/README.md b/plugins/common-aliases/README.md index d198a29ac..b6f34cb54 100644 --- a/plugins/common-aliases/README.md +++ b/plugins/common-aliases/README.md @@ -40,11 +40,13 @@ plugins=(... common-aliases) | Alias | Command | Description | |-------|-----------------------------------------------------|-----------------------------------------| -| fd | `find . -type d -name` | Find a directory with the given name | +| fd\* | `find . -type d -name` | Find a directory with the given name | | ff | `find . -type f -name` | Find a file with the given name | | grep | `grep --color` | Searches for a query string | | sgrep | `grep -R -n -H -C 5 --exclude-dir={.git,.svn,CVS}` | Useful for searching within files | +\* Only if the [`fd`](https://github.com/sharkdp/fd) command isn't installed. + ### Other Aliases | Alias | Command | Description | -- cgit v1.2.3-70-g09d2 From 79d0182a2666a98f04bd0d731012ce97114c1ef1 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Thu, 10 Dec 2020 20:38:19 +0100 Subject: refactor(hanami): change global aliases and clean up README --- plugins/hanami/README.md | 63 ++++++++++++++++++++++++---------------- plugins/hanami/hanami.plugin.zsh | 6 ++-- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/plugins/hanami/README.md b/plugins/hanami/README.md index 3ac8defbb..c9e09f4f2 100644 --- a/plugins/hanami/README.md +++ b/plugins/hanami/README.md @@ -1,32 +1,45 @@ -# Hanami Plugin # -This plugin adds convenient ways to work with [Hanami](https://hanamirb.org/) via console. +# Hanami Plugin + +This plugin adds convenient aliases to work with [Hanami](https://hanamirb.org/) via console. It's inspired by Rails plugin, so if you've used it, you'll feel like home. -## Usage ## +To use it, add `hanami` to the plugins array in your zshrc file: + +```zsh +plugins=(... hanami) +``` + +## Usage For example, type `hc` into your console when you're within Hanami project directory to run the application console. Have a look at available shortcuts below. You can read more about these commands [on the official website](https://hanamirb.org/guides/command-line/applications/). -## Aliases ## - -| Alias | Command | Description | -|-------|---------------------------|---------------------------------------------------------| -| HED | HANAMI_ENV=development | Set environment variable HANAMI_ENV to development | -| HEP | HANAMI_ENV=production | Set environment variable HANAMI_ENV to production | -| HET | HANAMI_ENV=test | Set environment variable HANAMI_ENV to test | -| hc | hanami console | Run application console | -| hd | hanami destroy | Remove specified hanami resource | -| hg | hanami generate | Create specified hanami resource | -| hgm | hanami generate migration | Create migration file | -| hs | hanami server | Launch server with hanami application | -| hsp | hanami server -p | Launch server with specified port | -| hr | hanami routes | List application routes | -| hdc | hanami db create | Create application database | -| hdd | hanami db drop | Delete application database | -| hdp | hanami db prepare | Prepare database for the current environment | -| hda | hanami db apply | Recreates a fresh schema after migrations (destructive) | -| hdv | hanami db version | Print current database version | -| hdrs | hdd && hdp | Drop and recreate application database | -| hdtp | HET hdp | Actualize test environment database | -| hrg | hr | grep | Grep hanami routes with specified pattern | +## Aliases + +| Alias | Command | Description | +|-------|-----------------------------|---------------------------------------------------------| +| HED\* | `HANAMI_ENV=development` | Set environment variable HANAMI_ENV to development | +| HEP\* | `HANAMI_ENV=production` | Set environment variable HANAMI_ENV to production | +| HET\* | `HANAMI_ENV=test` | Set environment variable HANAMI_ENV to test | +| hc | `hanami console` | Run application console | +| hd | `hanami destroy` | Remove specified hanami resource | +| hg | `hanami generate` | Create specified hanami resource | +| hgm | `hanami generate migration` | Create migration file | +| hs | `hanami server` | Launch server with hanami application | +| hsp | `hanami server -p` | Launch server with specified port | +| hr | `hanami routes` | List application routes | +| hdc | `hanami db create` | Create application database | +| hdd | `hanami db drop` | Delete application database | +| hdp | `hanami db prepare` | Prepare database for the current environment | +| hda | `hanami db apply` | Recreates a fresh schema after migrations (destructive) | +| hdv | `hanami db version` | Print current database version | +| hdrs | `hdd && hdp` | Drop and recreate application database | +| hdtp | `HET hdp` | Actualize test environment database | +| hrg | `hr | grep` | Grep hanami routes with specified pattern | + +\* You should use these at the beginning of a command, for example: + +```console +$ HED hdd # equivalent to 'HANAMI_ENV=development hanami db drop' +``` diff --git a/plugins/hanami/hanami.plugin.zsh b/plugins/hanami/hanami.plugin.zsh index 349c42cae..42143b428 100644 --- a/plugins/hanami/hanami.plugin.zsh +++ b/plugins/hanami/hanami.plugin.zsh @@ -1,6 +1,6 @@ -alias -g HED='HANAMI_ENV=development' -alias -g HEP='HANAMI_ENV=production' -alias -g HET='HANAMI_ENV=test' +alias HED='HANAMI_ENV=development ' +alias HEP='HANAMI_ENV=production ' +alias HET='HANAMI_ENV=test ' alias hc='hanami console' alias hd='hanami destroy' -- cgit v1.2.3-70-g09d2 From 2f39c68ab0157b55413de74e579d1787f26e58b6 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Thu, 10 Dec 2020 11:44:08 -0800 Subject: feat(lib): show upstream branch in `git_prompt_info` (#9188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show the remote branch the local branch is tracking if `ZSH_THEME_GIT_SHOW_UPSTREAM` is set, like so: `ZSH_THEME_GIT_SHOW_UPSTREAM=1`. Co-authored-by: Marc Cornellà --- lib/git.zsh | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/git.zsh b/lib/git.zsh index 53d39609e..da8a3e7a7 100644 --- a/lib/git.zsh +++ b/lib/git.zsh @@ -9,14 +9,27 @@ function __git_prompt_git() { GIT_OPTIONAL_LOCKS=0 command git "$@" } -# Outputs current branch info in prompt format function git_prompt_info() { + # If we are on a folder not tracked by git, get out. + # Otherwise, check for hide-info at global and local repository level + if ! __git_prompt_git rev-parse --git-dir &> /dev/null \ + || [[ "$(__git_prompt_git config --get oh-my-zsh.hide-info 2>/dev/null)" == 1 ]]; then + return 0 + fi + local ref - if [[ "$(__git_prompt_git config --get oh-my-zsh.hide-status 2>/dev/null)" != "1" ]]; then - ref=$(__git_prompt_git symbolic-ref HEAD 2> /dev/null) || \ - ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) || return 0 - echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$(parse_git_dirty)$ZSH_THEME_GIT_PROMPT_SUFFIX" + ref=$(__git_prompt_git symbolic-ref --short HEAD 2> /dev/null) \ + || ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) \ + || return 0 + + # Use global ZSH_THEME_GIT_SHOW_UPSTREAM=1 for including upstream remote info + local upstream + if (( ${+ZSH_THEME_GIT_SHOW_UPSTREAM} )); then + upstream=$(__git_prompt_git rev-parse --abbrev-ref --symbolic-full-name "@{upstream}" 2>/dev/null) \ + && upstream=" -> ${upstream}" fi + + echo "${ZSH_THEME_GIT_PROMPT_PREFIX}${ref}${upstream}$(parse_git_dirty)${ZSH_THEME_GIT_PROMPT_SUFFIX}" } # Checks if working tree is dirty -- cgit v1.2.3-70-g09d2 From 28265812f8fb923788d74409cef3e5635793b38e Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Thu, 10 Dec 2020 20:45:22 +0100 Subject: chore!: notify breaking change in `git_prompt_info` BREAKING CHANGE: the git config option to skip running `git_prompt_info` has changed from `oh-my-zsh.hide-status` to `oh-my-zsh.hide-info` (#9188). --- lib/git.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/git.zsh b/lib/git.zsh index da8a3e7a7..157c85062 100644 --- a/lib/git.zsh +++ b/lib/git.zsh @@ -21,7 +21,7 @@ function git_prompt_info() { ref=$(__git_prompt_git symbolic-ref --short HEAD 2> /dev/null) \ || ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) \ || return 0 - + # Use global ZSH_THEME_GIT_SHOW_UPSTREAM=1 for including upstream remote info local upstream if (( ${+ZSH_THEME_GIT_SHOW_UPSTREAM} )); then -- cgit v1.2.3-70-g09d2 From 918195425576e57016c8415766a7d4e98bc2e260 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Fri, 11 Dec 2020 16:54:22 +0100 Subject: fix(composer): autoload `_cache_invalid` for antigen compatibility Antigen doesn't call `compinit` until the user prompt is about to be shown, which means `_cache_invalid` isn't automatically autoloaded. This patch fixes that, though we should work towards a better caching mechanism. Fixes #9490 --- plugins/composer/composer.plugin.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/composer/composer.plugin.zsh b/plugins/composer/composer.plugin.zsh index 75b03ffe7..218a13e5f 100644 --- a/plugins/composer/composer.plugin.zsh +++ b/plugins/composer/composer.plugin.zsh @@ -51,7 +51,7 @@ alias cget='curl -s https://getcomposer.org/installer | php' # Add Composer's global binaries to PATH, using Composer if available. if (( $+commands[composer] )); then - autoload -Uz _store_cache _retrieve_cache + autoload -Uz _store_cache _retrieve_cache _cache_invalid _retrieve_cache composer -- cgit v1.2.3-70-g09d2 From 9d6b3984f92cf7f4411b40dfb5a0897b260ae368 Mon Sep 17 00:00:00 2001 From: Aaron Toponce Date: Sat, 12 Dec 2020 04:50:45 -0700 Subject: feat(plugins): add genpass plugin with 3 distinct password generators (#9502) --- .github/CODEOWNERS | 1 + plugins/genpass/README.md | 65 ++++++++++++++++++++++++++ plugins/genpass/genpass.plugin.zsh | 95 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 plugins/genpass/README.md create mode 100644 plugins/genpass/genpass.plugin.zsh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e175d549..b091f5d89 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,6 @@ # Plugin owners plugins/aws/ @maksyms +plugins/genpass/ @atoponce plugins/git-lfs/ @vietduc01100001 plugins/gitfast/ @felipec plugins/sdk/ @rgoldberg diff --git a/plugins/genpass/README.md b/plugins/genpass/README.md new file mode 100644 index 000000000..e6e7a5138 --- /dev/null +++ b/plugins/genpass/README.md @@ -0,0 +1,65 @@ +# genpass + +This plugin provides three unique password generators for ZSH. Each generator +has at least a 128-bit security margin and generates passwords from the +cryptographically secure `/dev/urandom`. Each generator can also take an +optional numeric argument to generate multiple passwords. + +Requirements: + +* `grep(1)` +* GNU coreutils (or appropriate for your system) +* Word list providing `/usr/share/dict/words` + +To use it, add `genpass` to the plugins array in your zshrc file: + + plugins=(... genpass) + +## genpass-apple + +Generates a pronounceable pseudoword passphrase of the "cvccvc" consonant/vowel +syntax, inspired by [Apple's iCloud Keychain password generator][1]. Each +pseudoword has exactly 1 digit placed at the edge of a "word" and exactly 1 +capital letter to satisfy most password security requirements. + + % genpass-apple + gelcyv-foqtam-fotqoh-viMleb-lexduv-6ixfuk + + % genpass-apple 3 + japvyz-qyjti4-kajrod-nubxaW-hukkan-dijcaf + vydpig-fucnul-3ukpog-voggom-zygNad-jepgad + zocmez-byznis-hegTaj-jecdyq-qiqmiq-5enwom + +[1]: https://developer.apple.com/password-rules/ + +## genpass-monkey + +Generates visually unambiguous random meaningless strings using [Crockford's +base32][2]. + + % genpass-monkey + xt7gn976e7jj3fstgpy27330x3 + + % genpass-monkey 3 + n1qqwtzgejwgqve9yzf2gxvx4m + r2n3f5s6vbqs2yx7xjnmahqewy + 296w9y9rts3p5r9yay0raek8e5 + +[2]: https://www.crockford.com/base32.html + +## genpass-xkcd + +Generates passphrases from `/usr/share/dict/words` inspired by the [famous (and +slightly misleading) XKCD comic][3]. Each passphrase is prepended with a digit +showing the number of words in the passphrase to adhere to password security +requirements that require digits. Each word is 6 characters or less. + + % genpass-xkcd + 9-eaten-Slav-rife-aired-hill-cordon-splits-welsh-napes + + % genpass-xkcd 3 + 9-worker-Vlad-horde-shrubs-smite-thwart-paw-alters-prawns + 9-tutors-stink-rhythm-junk-snappy-hooray-barbs-mewl-clomp + 9-vital-escape-Angkor-Huff-wet-Mayra-abbés-putts-guzzle + +[3]: https://xkcd.com/936/ diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh new file mode 100644 index 000000000..15bfebda8 --- /dev/null +++ b/plugins/genpass/genpass.plugin.zsh @@ -0,0 +1,95 @@ +autoload -U regexp-replace +zmodload zsh/mathfunc + +genpass-apple() { + # Generates a 128-bit password of 6 pseudowords of 6 characters each + # EG, xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp + # Can take a numerical argument for generating extra passwords + local -i i j num + + [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 + + local consonants="$(LC_ALL=C tr -cd b-df-hj-np-tv-xz < /dev/urandom \ + | head -c $((24*$num)))" + local vowels="$(LC_ALL=C tr -cd aeiouy < /dev/urandom | head -c $((12*$num)))" + local digits="$(LC_ALL=C tr -cd 0-9 < /dev/urandom | head -c $num)" + + # The digit is placed on a pseudoword edge using $base36. IE, Dvccvc or cvccvD + local position="$(LC_ALL=C tr -cd 056bchinotuz < /dev/urandom | head -c $num)" + local -A base36=(0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 \ + e 14 f 15 g 16 h 17 i 18 j 19 k 20 l 21 m 22 n 23 o 24 p 25 q 26 r 27 s 28 \ + t 29 u 30 v 31 w 32 x 33 y 34 z 35) + + for i in {1..$num}; do + local pseudo="" + + for j in {1..12}; do + # Uniformly iterate through $consonants and $vowels for each $i and $j + # Creates cvccvccvccvccvccvccvccvccvccvccvccvc for each $num + pseudo="${pseudo}${consonants:$((24*$i+2*${j}-26)):1}" + pseudo="${pseudo}${vowels:$((12*$i+${j}-13)):1}" + pseudo="${pseudo}${consonants:$((24*$i+2*${j}-25)):1}" + done + + local -i digit_pos=${base36[${position[$i]}]} + local -i char_pos=$digit_pos + + # The digit and uppercase character must be in different locations + while [[ $digit_pos == $char_pos ]]; do + char_pos=$base36[$(LC_ALL=C tr -cd 0-9a-z < /dev/urandom | head -c 1)] + done + + # Places the digit on a pseudoword edge + regexp-replace pseudo "^(.{$digit_pos}).(.*)$" \ + '${match[1]}${digits[$i]}${match[2]}' + + # Uppercase a random character (that is not a digit) + regexp-replace pseudo "^(.{$char_pos})(.)(.*)$" \ + '${match[1]}${(U)match[2]}${match[3]}' + + # Hyphenate each 6-character pseudoword + regexp-replace pseudo '^(.{6})(.{6})(.{6})(.{6})(.{6})(.{6})$' \ + '${match[1]}-${match[2]}-${match[3]}-${match[4]}-${match[5]}-${match[6]}' + + printf "${pseudo}\n" + done +} + +genpass-monkey() { + # Generates a 128-bit base32 password as if monkeys banged the keyboard + # EG, nz5ej2kypkvcw0rn5cvhs6qxtm + # Can take a numerical argument for generating extra passwords + local -i i num + + [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 + + local pass=$(LC_ALL=C tr -cd '0-9a-hjkmnp-tv-z' < /dev/urandom \ + | head -c $((26*$num))) + + for i in {1..$num}; do + printf "${pass:$((26*($i-1))):26}\n" + done +} + +genpass-xkcd() { + # Generates a 128-bit XKCD-style passphrase + # EG, 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster + # Can take a numerical argument for generating extra passwords + local -i i num + + [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 + + # Get all alphabetic words of at most 6 characters in length + local dict=$(grep -E '^[a-zA-Z]{,6}$' /usr/share/dict/words) + + # Calculate the base-2 entropy of each word in $dict + # Entropy is e = L * log2(C), where L is the length of the password (here, + # in words) and C the size of the character set (here, words in $dict). + # Solve for e = 128 bits of entropy. Recall: log2(n) = log(n)/log(2). + local -i n=$((int(ceil(128*log(2)/log(${(w)#dict}))))) + + for i in {1..$num}; do + printf "$n-" + printf "$dict" | shuf -n "$n" | paste -sd '-' + done +} -- cgit v1.2.3-70-g09d2 From e8e37eedbc8cf5e446be11971769df7cc10faeb0 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Sat, 12 Dec 2020 13:41:29 +0100 Subject: feat(updater): save version prior to updating so `omz changelog` just works™ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #9505 --- tools/changelog.sh | 11 +++++++++-- tools/upgrade.sh | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tools/changelog.sh b/tools/changelog.sh index 4ce42e5a3..845b1d4b4 100755 --- a/tools/changelog.sh +++ b/tools/changelog.sh @@ -340,16 +340,23 @@ function display-release { function main { # $1 = until commit, $2 = since commit - # $3 = output format (--raw|--text|--md) local until="$1" since="$2" + + # $3 = output format (--text|--raw|--md) + # --md: uses markdown formatting + # --raw: outputs without style + # --text: uses ANSI escape codes to style the output local output=${${3:-"--text"}#--*} if [[ -z "$until" ]]; then until=HEAD fi - # If $since is not specified, look up first version tag before $until if [[ -z "$since" ]]; then + # If $since is not specified: + # 1) try to find the version used before updating + # 2) try to find the first version tag before $until + since=$(command git config --get oh-my-zsh.lastVersion 2>/dev/null) || \ since=$(command git describe --abbrev=0 --tags "$until^" 2>/dev/null) || \ unset since elif [[ "$since" = --all ]]; then diff --git a/tools/upgrade.sh b/tools/upgrade.sh index cfd424527..4df7eb184 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -10,7 +10,7 @@ cd "$ZSH" # and that terminal supports them. local -a RAINBOW -local RED GREEN YELLOW BLUE UNDER BOLD RESET +local RED GREEN YELLOW BLUE BOLD DIM UNDER RESET if [ -t 1 ]; then RAINBOW=( @@ -28,6 +28,7 @@ if [ -t 1 ]; then YELLOW=$(printf '\033[33m') BLUE=$(printf '\033[34m') BOLD=$(printf '\033[1m') + DIM=$(printf '\033[2m') UNDER=$(printf '\033[4m') RESET=$(printf '\033[m') fi @@ -69,6 +70,9 @@ if git pull --rebase --stat origin master; then else message="Hooray! Oh My Zsh has been updated!" + # Save the commit prior to updating + git config oh-my-zsh.lastVersion "$last_commit" + # Display changelog with less if available, otherwise just print it to the terminal if [[ "$1" = --interactive ]]; then if (( $+commands[less] )); then @@ -77,6 +81,8 @@ if git pull --rebase --stat origin master; then "$ZSH/tools/changelog.sh" HEAD "$last_commit" fi fi + + printf "${BLUE}%s \`${BOLD}%s${RESET}${BLUE}\`${RESET}\n" "You can see the changelog again with" "omz changelog" fi printf '%s %s__ %s %s %s %s %s__ %s\n' $RAINBOW $RESET -- cgit v1.2.3-70-g09d2 From e2c73cf59a1a02257203db784f1e2fd27c830173 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Sat, 12 Dec 2020 14:22:26 +0100 Subject: fix(changelog): allow breaking change message to span multiple lines This also allows the option to put extra paragraphs after the BREAKING CHANGE message while properly displaying the breaking change message. Useful, for example, to add signed-off or co-authored lines. --- tools/changelog.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/changelog.sh b/tools/changelog.sh index 845b1d4b4..8753212e9 100755 --- a/tools/changelog.sh +++ b/tools/changelog.sh @@ -86,11 +86,15 @@ function parse-commit { # Return subject if the body or subject match the breaking change format function commit:is-breaking { - local subject="$1" body="$2" + local subject="$1" body="$2" message if [[ "$body" =~ "BREAKING CHANGE: (.*)" || \ "$subject" =~ '^[^ :\)]+\)?!: (.*)$' ]]; then - echo "${match[1]}" + message="${match[1]}" + # skip next paragraphs (separated by two newlines or more) + message="${message%%$'\n\n'*}" + # ... and replace newlines with spaces + echo "${message//$'\n'/ }" else return 1 fi -- cgit v1.2.3-70-g09d2 From f8e9339c87ab6b17ee34d34a72ddbe89333e9fa1 Mon Sep 17 00:00:00 2001 From: Aaron Toponce Date: Sat, 12 Dec 2020 17:20:41 -0700 Subject: fix(genpass): only use words with ASCII characters in `genpass-xkcd` (#9508) --- plugins/genpass/genpass.plugin.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh index 15bfebda8..65bbd2e48 100644 --- a/plugins/genpass/genpass.plugin.zsh +++ b/plugins/genpass/genpass.plugin.zsh @@ -80,7 +80,7 @@ genpass-xkcd() { [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 # Get all alphabetic words of at most 6 characters in length - local dict=$(grep -E '^[a-zA-Z]{,6}$' /usr/share/dict/words) + local dict=$(LC_ALL=C grep -E '^[a-zA-Z]{,6}$' /usr/share/dict/words) # Calculate the base-2 entropy of each word in $dict # Entropy is e = L * log2(C), where L is the length of the password (here, -- cgit v1.2.3-70-g09d2 From 2a0ae3315db98d137de547e2cb9adfbc38263e6c Mon Sep 17 00:00:00 2001 From: Matt Lewin Date: Sun, 13 Dec 2020 16:28:22 -0500 Subject: fix(genpass): fix grep regex in `genpass-xkcd` for FreeBSD version (#9514) --- plugins/genpass/genpass.plugin.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh index 65bbd2e48..e7f86bf7a 100644 --- a/plugins/genpass/genpass.plugin.zsh +++ b/plugins/genpass/genpass.plugin.zsh @@ -80,7 +80,7 @@ genpass-xkcd() { [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 # Get all alphabetic words of at most 6 characters in length - local dict=$(LC_ALL=C grep -E '^[a-zA-Z]{,6}$' /usr/share/dict/words) + local dict=$(LC_ALL=C grep -E '^[a-zA-Z]{1,6}$' /usr/share/dict/words) # Calculate the base-2 entropy of each word in $dict # Entropy is e = L * log2(C), where L is the length of the password (here, -- cgit v1.2.3-70-g09d2 From 619097cc2ad31c1b5086870293739d41dd4129c3 Mon Sep 17 00:00:00 2001 From: Patrick Harrison Date: Mon, 14 Dec 2020 09:52:02 +0700 Subject: fix(genpass): check for presence of shuf command. "shuf" is not a standard command on MacOS and requires installation of the brew coreutils package --- plugins/genpass/genpass.plugin.zsh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh index e7f86bf7a..f1ad80bba 100644 --- a/plugins/genpass/genpass.plugin.zsh +++ b/plugins/genpass/genpass.plugin.zsh @@ -75,6 +75,12 @@ genpass-xkcd() { # Generates a 128-bit XKCD-style passphrase # EG, 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster # Can take a numerical argument for generating extra passwords + + if (( ! $+commands[shuf] )); then + echo >&2 "$0: \`shuf\` command not found. Install coreutils (\`brew install coreutils\` on macOS)." + return 1 + fi + local -i i num [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 -- cgit v1.2.3-70-g09d2 From 076f7f1eb19914877e49eb186eb076fc3e493b36 Mon Sep 17 00:00:00 2001 From: Marc Cornellà Date: Mon, 14 Dec 2020 15:42:10 +0100 Subject: fix(genpass): warn if no wordlist is found --- plugins/genpass/genpass.plugin.zsh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh index f1ad80bba..1353ef456 100644 --- a/plugins/genpass/genpass.plugin.zsh +++ b/plugins/genpass/genpass.plugin.zsh @@ -73,7 +73,7 @@ genpass-monkey() { genpass-xkcd() { # Generates a 128-bit XKCD-style passphrase - # EG, 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster + # e.g, 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster # Can take a numerical argument for generating extra passwords if (( ! $+commands[shuf] )); then @@ -81,6 +81,11 @@ genpass-xkcd() { return 1 fi + if [[ ! -e /usr/share/dict/words ]]; then + echo >&2 "$0: no wordlist found in \`/usr/share/dict/words\`. Install one first." + return 1 + fi + local -i i num [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 -- cgit v1.2.3-70-g09d2 From 2db42c6ce745ed37262bed6c97683a00a430f076 Mon Sep 17 00:00:00 2001 From: Patrick Harrison Date: Mon, 14 Dec 2020 10:11:12 +0700 Subject: fix(genpass): add compatibility for macOS paste command "paste" on macOS requires a '-' to signify that the standard input is used. Without the '-' character, the command errors out. --- plugins/genpass/genpass.plugin.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh index 1353ef456..e6a1cef34 100644 --- a/plugins/genpass/genpass.plugin.zsh +++ b/plugins/genpass/genpass.plugin.zsh @@ -101,6 +101,6 @@ genpass-xkcd() { for i in {1..$num}; do printf "$n-" - printf "$dict" | shuf -n "$n" | paste -sd '-' + printf "$dict" | shuf -n "$n" | paste -sd '-' - done } -- cgit v1.2.3-70-g09d2 From b28665aebb4c1b07a57890eb59551bc51d0acf37 Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Wed, 16 Dec 2020 16:57:59 +0100 Subject: fix(genpass): improve performance and usability and fix bugs (#9520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Bugs* The following bugs have been fixed: - All generators ignored errors from external commands. For example, if `/usr/share/dict/words` was unreadable, `genpass-xkcd` would print "0-" as a password and return success. - All generators silently ignored the argument if it wasn't a number. For example, `genpass-apple -2` was generating one password and not printing any errors. - All generators silently ignored extra arguments. For example, `genpass-apple -n 2` was generating one password and not printing any errors. - `genpass-xkcd` was generating passwords with less than 128 bits of security margin in contradiction to documentation. The smaller the dictionary size, the weaker the passwords it was generating. For a dictionary with 27 words, `genpass-xkcd` was generating passwords with 93 bits of security margin (`log2(27!)`). - The source of random data used by `genpass-xkcd` was not cryptographically secure in contradiction to documentation. See: https://www.gnu.org/software/coreutils/manual/html_node/Random-sources.html - `genpass-apple` could generate a password with non-ascii characters depending on user locale. For example, passwords could contain 'İ' for users with Turkish locale. - `genpass-apple` didn't work with `ksh_arrays` shell option. - `genpass-xkcd` was printing spurious errors with `ksh_arrays` shell option. - `genpass-xkcd` was producing too short (weak) or too strong (long) and/or printing errors when `IFS` was set to non-default value. - All generators were printing fewer passwords than requested and returning success when passed a very large number as an argument. *Usability* Generators are now implemented as self-contained executable files. They can be invoked from scripts with no additional setup. Generators no longer depend on external commands. The only dependencies are `/dev/urandom` and, for `genpass-xkcd`, `/usr/share/dict/words`. All generators used to silently ignore all arguments after the first and the first argument if it wasn't a number. For example, both `genpass-apple -2` and `genpass-apple -n 2` were generating one password and not printing any errors. Now these print an error and fail. *Performance* The time it takes to load the plugin has been greatly reduced. This translates into faster zsh startup when the plugin is enabled. Incidentally, two generators out of three have been sped up to a large degree while one generator (`genpass-xkcd`) has gotten slower. This is unlikely to matter one way or another unless generating a very large number of passwords. In the latter case `genpass-xkcd` is now also faster than it used to be. The following table shows benchmark results from Linux x86-64 on i9-7900X. The numbers in the second and third columns show how many times a given command could be executed per second. Higher numbers are better. command | before (Hz) | after (Hz) | speedup | ----------------------------|------------:|-----------:|--------:| `source genpass.plugin.zsh` | 4810 | 68700 | +1326% | `genpass-apple` | 30.3 | 893 | +2846% | `genpass-monkey` | 203 | 5290 | +2504% | `genpass-xkcd` | 34.4 | 14.5 | -58% | `genpass-xkcd 1000` | 0.145 | 0.804 | +454% | --- plugins/genpass/README.md | 15 +++--- plugins/genpass/genpass-apple | 79 +++++++++++++++++++++++++++ plugins/genpass/genpass-monkey | 32 +++++++++++ plugins/genpass/genpass-xkcd | 68 +++++++++++++++++++++++ plugins/genpass/genpass.plugin.zsh | 107 +------------------------------------ 5 files changed, 188 insertions(+), 113 deletions(-) create mode 100755 plugins/genpass/genpass-apple create mode 100755 plugins/genpass/genpass-monkey create mode 100755 plugins/genpass/genpass-xkcd diff --git a/plugins/genpass/README.md b/plugins/genpass/README.md index e6e7a5138..a5ff4a876 100644 --- a/plugins/genpass/README.md +++ b/plugins/genpass/README.md @@ -5,21 +5,22 @@ has at least a 128-bit security margin and generates passwords from the cryptographically secure `/dev/urandom`. Each generator can also take an optional numeric argument to generate multiple passwords. -Requirements: +To use it from an interactive ZSH, add `genpass` to the plugins array in your +zshrc file: -* `grep(1)` -* GNU coreutils (or appropriate for your system) -* Word list providing `/usr/share/dict/words` + plugins=(... genpass) -To use it, add `genpass` to the plugins array in your zshrc file: +You can also invoke password generators directly (they are implemented as +standalone executable files), which can be handy when you need to generate +passwords in a script: - plugins=(... genpass) + ~/.oh-my-zsh/plugins/genpass/genpass-apple 3 ## genpass-apple Generates a pronounceable pseudoword passphrase of the "cvccvc" consonant/vowel syntax, inspired by [Apple's iCloud Keychain password generator][1]. Each -pseudoword has exactly 1 digit placed at the edge of a "word" and exactly 1 +password has exactly 1 digit placed at the edge of a "word" and exactly 1 capital letter to satisfy most password security requirements. % genpass-apple diff --git a/plugins/genpass/genpass-apple b/plugins/genpass/genpass-apple new file mode 100755 index 000000000..963ab6447 --- /dev/null +++ b/plugins/genpass/genpass-apple @@ -0,0 +1,79 @@ +#!/usr/bin/env zsh +# +# Usage: genpass-apple [NUM] +# +# Generate a password made of 6 pseudowords of 6 characters each +# with the security margin of at least 128 bits. +# +# Example password: xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp +# +# If given a numerical argument, generate that many passwords. + +emulate -L zsh -o no_unset -o warn_create_global -o warn_nested_var + +if [[ ARGC -gt 1 || ${1-1} != ${~:-<1-$((16#7FFFFFFF))>} ]]; then + print -ru2 -- "usage: $0 [NUM]" + return 1 +fi + +zmodload zsh/system zsh/mathfunc || return + +{ + local -r vowels=aeiouy + local -r consonants=bcdfghjklmnpqrstvwxz + local -r digits=0123456789 + + # Sets REPLY to a uniformly distributed random number in [1, $1]. + # Requires: $1 <= 256. + function -$0-rand() { + local c + while true; do + sysread -s1 c || return + # Avoid bias towards smaller numbers. + (( #c < 256 / $1 * $1 )) && break + done + typeset -g REPLY=$((#c % $1 + 1)) + } + + local REPLY chars + + repeat ${1-1}; do + # Generate 6 pseudowords of the form cvccvc where c and v + # denote random consonants and vowels respectively. + local words=() + repeat 6; do + words+=('') + repeat 2; do + for chars in $consonants $vowels $consonants; do + -$0-rand $#chars || return + words[-1]+=$chars[REPLY] + done + done + done + + local pwd=${(j:-:)words} + + # Replace either the first or the last character in one of + # the words with a random digit. + -$0-rand $#digits || return + local digit=$digits[REPLY] + -$0-rand $((2 * $#words)) || return + pwd[REPLY/2*7+2*(REPLY%2)-1]=$digit + + # Convert one lower-case character to upper case. + while true; do + -$0-rand $#pwd || return + [[ $vowels$consonants == *$pwd[REPLY]* ]] && break + done + # NOTE: We aren't using ${(U)c} here because its results are + # locale-dependent. For example, when upper-casing 'i' in Turkish + # locale we would get 'İ', a.k.a. latin capital letter i with dot + # above. We could set LC_CTYPE=C locally but then we would run afoul + # of this zsh bug: https://www.zsh.org/mla/workers/2020/msg00588.html. + local c=$pwd[REPLY] + printf -v c '%o' $((#c - 32)) + printf "%s\\$c%s\\n" "$pwd[1,REPLY-1]" "$pwd[REPLY+1,-1]" || return + done +} always { + unfunction -m -- "-${(b)0}-*" +} } ]]; then + print -ru2 -- "usage: $0 [NUM]" + return 1 +fi + +zmodload zsh/system || return + +{ + local -r chars=abcdefghjkmnpqrstvwxyz0123456789 + local c + repeat ${1-1}; do + repeat 26; do + sysread -s1 c || return + # There is uniform because $#chars divides 256. + print -rn -- $chars[#c%$#chars+1] + done + print + done +} } ]]; then + print -ru2 -- "usage: $0 [NUM]" + return 1 +fi + +zmodload zsh/system zsh/mathfunc || return + +local -r dict=/usr/share/dict/words + +if [[ ! -e $dict ]]; then + print -ru2 -- "$0: file not found: $dict" + return 1 +fi + +# Read all dictionary words and leave only those made of 1-6 characters. +local -a words +words=(${(M)${(f)"$(<$dict)"}:#[a-zA-Z](#c1,6)}) || return + +if (( $#words < 2 )); then + print -ru2 -- "$0: not enough suitable words in $dict" + return 1 +fi + +if (( $#words > 16#7FFFFFFF )); then + print -ru2 -- "$0: too many words in $dict" + return 1 +fi + +# Figure out how many words we need for 128 bits of security margin. +# Each word adds log2($#words) bits. +local -i n=$((ceil(128. / log2($#words)))) + +{ + local c + repeat ${1-1}; do + print -rn -- $n + repeat $n; do + while true; do + # Generate a random number in [0, 2**31). + local -i rnd=0 + repeat 4; do + sysread -s1 c || return + (( rnd = (~(1 << 23) & rnd) << 8 | #c )) + done + # Avoid bias towards words in the beginning of the list. + (( rnd < 16#7FFFFFFF / $#words * $#words )) || continue + print -rn -- -$words[rnd%$#words+1] + break + done + done + print + done +} &2 "$0: \`shuf\` command not found. Install coreutils (\`brew install coreutils\` on macOS)." - return 1 - fi - - if [[ ! -e /usr/share/dict/words ]]; then - echo >&2 "$0: no wordlist found in \`/usr/share/dict/words\`. Install one first." - return 1 - fi - - local -i i num - - [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1 - - # Get all alphabetic words of at most 6 characters in length - local dict=$(LC_ALL=C grep -E '^[a-zA-Z]{1,6}$' /usr/share/dict/words) - - # Calculate the base-2 entropy of each word in $dict - # Entropy is e = L * log2(C), where L is the length of the password (here, - # in words) and C the size of the character set (here, words in $dict). - # Solve for e = 128 bits of entropy. Recall: log2(n) = log(n)/log(2). - local -i n=$((int(ceil(128*log(2)/log(${(w)#dict}))))) - - for i in {1..$num}; do - printf "$n-" - printf "$dict" | shuf -n "$n" | paste -sd '-' - - done -} +autoload -Uz genpass-apple genpass-monkey genpass-xkcd -- cgit v1.2.3-70-g09d2