chezmoi init

This commit is contained in:
Cy Pokhrel 2024-10-22 11:11:45 -04:00
commit 530d6d7195
No known key found for this signature in database
GPG key ID: 1200FBE36C2ADE2E
1176 changed files with 111325 additions and 0 deletions

View file

@ -0,0 +1,297 @@
# 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"
fi
# Get user's update preferences
#
# Supported update modes:
# - prompt (default): the user is asked before updating when it's time to update
# - auto: the update is performed automatically when it's time
# - reminder: a reminder is shown to the user when it's time to update
# - background-alpha: an experimental update-on-the-background option
# - disabled: automatic update is turned off
zstyle -s ':omz:update' mode update_mode || {
update_mode=prompt
# If the mode zstyle setting is not set, support old-style settings
[[ "$DISABLE_UPDATE_PROMPT" != true ]] || update_mode=auto
[[ "$DISABLE_AUTO_UPDATE" != true ]] || update_mode=disabled
}
# Cancel update if:
# - the automatic update is disabled
# - the current user doesn't have write permissions nor owns the $ZSH directory
# - is not run from a tty
# - git is unavailable on the system
# - $ZSH is not a git repository
if [[ "$update_mode" = disabled ]] \
|| [[ ! -w "$ZSH" || ! -O "$ZSH" ]] \
|| [[ ! -t 1 ]] \
|| ! command git --version 2>&1 >/dev/null \
|| (builtin cd -q "$ZSH"; ! command git rev-parse --is-inside-work-tree &>/dev/null); then
unset update_mode
return
fi
function current_epoch() {
zmodload zsh/datetime
echo $(( EPOCHSECONDS / 60 / 60 / 24 ))
}
function is_update_available() {
local branch
branch=${"$(builtin cd -q "$ZSH"; git config --local oh-my-zsh.branch)":-master}
local remote remote_url remote_repo
remote=${"$(builtin cd -q "$ZSH"; git config --local oh-my-zsh.remote)":-origin}
remote_url=$(builtin cd -q "$ZSH"; git config remote.$remote.url)
local repo
case "$remote_url" in
https://github.com/*) repo=${${remote_url#https://github.com/}%.git} ;;
git@github.com:*) repo=${${remote_url#git@github.com:}%.git} ;;
*)
# If the remote is not using GitHub we can't check for updates
# Let's assume there are updates
return 0 ;;
esac
# If the remote repo is not the official one, let's assume there are updates available
[[ "$repo" = ohmyzsh/ohmyzsh ]] || return 0
local api_url="https://api.github.com/repos/${repo}/commits/${branch}"
# Get local HEAD. If this fails assume there are updates
local local_head
local_head=$(builtin cd -q "$ZSH"; git rev-parse $branch 2>/dev/null) || return 0
# Get remote HEAD. If no suitable command is found assume there are updates
# On any other error, skip the update (connection may be down)
local remote_head
remote_head=$(
if (( ${+commands[curl]} )); then
curl --connect-timeout 2 -fsSL -H 'Accept: application/vnd.github.v3.sha' $api_url 2>/dev/null
elif (( ${+commands[wget]} )); then
wget -T 2 -O- --header='Accept: application/vnd.github.v3.sha' $api_url 2>/dev/null
elif (( ${+commands[fetch]} )); then
HTTP_ACCEPT='Accept: application/vnd.github.v3.sha' fetch -T 2 -o - $api_url 2>/dev/null
else
exit 0
fi
) || return 1
# Compare local and remote HEADs (if they're equal there are no updates)
[[ "$local_head" != "$remote_head" ]] || return 1
# If local and remote HEADs don't match, check if there's a common ancestor
# If the merge-base call fails, $remote_head might not be downloaded so assume there are updates
local base
base=$(builtin cd -q "$ZSH"; git merge-base $local_head $remote_head 2>/dev/null) || return 0
# If the common ancestor ($base) is not $remote_head,
# the local HEAD is older than the remote HEAD
[[ $base != $remote_head ]]
}
function update_last_updated_file() {
local exit_status="$1" error="$2"
if [[ -z "${1}${2}" ]]; then
echo "LAST_EPOCH=$(current_epoch)" >! "${ZSH_CACHE_DIR}/.zsh-update"
return
fi
cat >! "${ZSH_CACHE_DIR}/.zsh-update" <<EOD
LAST_EPOCH=$(current_epoch)
EXIT_STATUS=${exit_status}
ERROR='${error//\'/’}'
EOD
}
function update_ohmyzsh() {
local verbose_mode
zstyle -s ':omz:update' verbose verbose_mode || verbose_mode=default
if [[ "$update_mode" != background-alpha ]] \
&& LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v $verbose_mode; then
update_last_updated_file
return $?
fi
local exit_status error
if error=$(LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v silent 2>&1); then
update_last_updated_file 0 "Update successful"
else
exit_status=$?
update_last_updated_file $exit_status "$error"
return $exit_status
fi
}
function has_typed_input() {
# Created by Philippe Troin <phil@fifi.org>
# https://zsh.org/mla/users/2022/msg00062.html
emulate -L zsh
zmodload zsh/zselect
# Back up stty settings prior to disabling canonical mode
# Consider that no input can be typed if stty fails
# (this might happen if stdin is not a terminal)
local termios
termios=$(stty --save 2>/dev/null) || return 1
{
# Disable canonical mode so that typed input counts
# regardless of whether Enter was pressed
stty -icanon
# Poll stdin (fd 0) for data ready to be read
zselect -t 0 -r 0
return $?
} always {
# Restore stty settings
stty $termios
}
}
function handle_update() {
() {
emulate -L zsh
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
fi
# Check for lock directory
if ! command mkdir "$ZSH/log/update.lock" 2>/dev/null; then
return
fi
# Remove lock directory on exit. `return $ret` 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.
# This means that for a CTRL+C, the trap needs to return the same exit status so that
# the shell actually exits what it's running.
trap "
ret=\$?
unset update_mode
unset -f current_epoch is_update_available update_last_updated_file update_ohmyzsh handle_update 2>/dev/null
command rm -rf '$ZSH/log/update.lock'
return \$ret
" 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
# Number of days before trying to update again
zstyle -s ':omz:update' frequency epoch_target || 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
# Test if Oh My Zsh directory is a git repository
if ! (builtin cd -q "$ZSH" && LANG= git rev-parse &>/dev/null); then
echo >&2 "[oh-my-zsh] Can't update: not a git repository."
return
fi
# Check if there are updates available before proceeding
if ! is_update_available; then
update_last_updated_file
return
fi
# If in reminder mode or user has typed input, show reminder and exit
if [[ "$update_mode" = reminder ]] || { [[ "$update_mode" != background-alpha ]] && has_typed_input }; then
printf '\r\e[0K' # move cursor to first column and clear whole line
echo "[oh-my-zsh] It's time to update! You can do that by running \`omz update\`"
return 0
fi
# Don't ask for confirmation before updating if in auto mode
if [[ "$update_mode" = (auto|background-alpha) ]]; then
update_ohmyzsh
return $?
fi
# Ask for confirmation and only update on 'y', 'Y' or Enter
# Otherwise just show a reminder for how to update
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 ;&
*) echo "[oh-my-zsh] You can update manually by running \`omz update\`" ;;
esac
}
unset update_mode
unset -f current_epoch is_update_available update_last_updated_file update_ohmyzsh handle_update
}
case "$update_mode" in
background-alpha)
autoload -Uz add-zsh-hook
_omz_bg_update() {
# do the update in a subshell
(handle_update) &|
# register update results function
add-zsh-hook precmd _omz_bg_update_status
# deregister background function
add-zsh-hook -d precmd _omz_bg_update
unset -f _omz_bg_update
}
_omz_bg_update_status() {
{
local LAST_EPOCH EXIT_STATUS ERROR
if [[ ! -f "$ZSH_CACHE_DIR"/.zsh-update ]]; then
return 1
fi
# check update results until timeout is reached
. "$ZSH_CACHE_DIR/.zsh-update"
if [[ -z "$EXIT_STATUS" || -z "$ERROR" ]]; then
return 1
fi
if [[ "$EXIT_STATUS" -eq 0 ]]; then
print -P "\n%F{green}[oh-my-zsh] Update successful.%f"
return 0
elif [[ "$EXIT_STATUS" -ne 0 ]]; then
print -P "\n%F{red}[oh-my-zsh] There was an error updating:%f"
printf "\n${fg[yellow]}%s${reset_color}" "$ERROR"
return 0
fi
} always {
if (( TRY_BLOCK_ERROR == 0 )); then
# if last update results have been handled, remove them from the status file
update_last_updated_file
# deregister background function
add-zsh-hook -d precmd _omz_bg_update_status
unset -f _omz_bg_update_status
fi
}
}
add-zsh-hook precmd _omz_bg_update
;;
*)
handle_update ;;
esac

View file

@ -0,0 +1,579 @@
#!/usr/bin/env zsh
cd "$ZSH"
setopt extendedglob
##############################
# 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.
local -a IGNORED_TYPES
IGNORED_TYPES=(${${${(@k)TYPES}:|MAIN_TYPES}:|OTHER_TYPES})
############################
# 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
# Parse commit type from the subject
if [[ "$1" =~ '^([a-zA-Z_\-]+)(\(.+\))?!?: .+$' ]]; then
type="${match[1]}"
fi
# If $type doesn't appear in $TYPES array mark it as 'other'
if [[ -n "$type" && -n "${(k)TYPES[(i)$type]}" ]]; then
echo $type
else
echo other
fi
}
function commit:scope {
local scope
# Try to find scope in "type(<scope>):" format
if [[ "$1" =~ '^[a-zA-Z_\-]+\((.+)\)!?: .+$' ]]; then
echo "${match[1]}"
return
fi
# If no scope found, try to find it in "<scope>:" format
if [[ "$1" =~ '^([a-zA-Z_\-]+): .+$' ]]; then
scope="${match[1]}"
# Make sure it's not a type before printing it
if [[ -z "${(k)TYPES[(i)$scope]}" ]]; then
echo "$scope"
fi
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.
if [[ "$1" =~ '^[a-zA-Z_\-]+(\(.+\))?!?: (.+)$' ]]; then
echo "${match[2]}"
else
echo "$1"
fi
}
# Return subject if the body or subject match the breaking change format
function commit:is-breaking {
local subject="$1" body="$2" message
if [[ "$body" =~ "BREAKING CHANGE: (.*)" || \
"$subject" =~ '^[^ :\)]+\)?!: (.*)$' ]]; then
message="${match[1]}"
# remove CR characters (might be inserted in GitHub UI commit description form)
message="${message//$'\r'/}"
# remove lines containing only whitespace
local nlnl=$'\n\n'
message="${message//$'\n'[[:space:]]##$'\n'/$nlnl}"
# 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
}
# 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="$2" body="$3" warning rhash
# 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
types[$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
}
################################
# SUPPORTS HYPERLINKS FUNCTION #
################################
# The code for checking if a terminal supports hyperlinks is copied from install.sh
# The [ -t 1 ] check only works when the function is not called from
# a subshell (like in `$(...)` or `(...)`, so this hack redefines the
# function at the top level to always return false when stdout is not
# a tty.
if [ -t 1 ]; then
is_tty() {
true
}
else
is_tty() {
false
}
fi
# This function uses the logic from supports-hyperlinks[1][2], which is
# made by Kat Marchán (@zkat) and licensed under the Apache License 2.0.
# [1] https://github.com/zkat/supports-hyperlinks
# [2] https://crates.io/crates/supports-hyperlinks
#
# Copyright (c) 2021 Kat Marchán
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
supports_hyperlinks() {
# $FORCE_HYPERLINK must be set and be non-zero (this acts as a logic bypass)
if [ -n "$FORCE_HYPERLINK" ]; then
[ "$FORCE_HYPERLINK" != 0 ]
return $?
fi
# If stdout is not a tty, it doesn't support hyperlinks
is_tty || return 1
# DomTerm terminal emulator (domterm.org)
if [ -n "$DOMTERM" ]; then
return 0
fi
# VTE-based terminals above v0.50 (Gnome Terminal, Guake, ROXTerm, etc)
if [ -n "$VTE_VERSION" ]; then
[ $VTE_VERSION -ge 5000 ]
return $?
fi
# If $TERM_PROGRAM is set, these terminals support hyperlinks
case "$TERM_PROGRAM" in
Hyper|iTerm.app|terminology|WezTerm|vscode) return 0 ;;
esac
# These termcap entries support hyperlinks
case "$TERM" in
xterm-kitty|alacritty|alacritty-direct) return 0 ;;
esac
# xfce4-terminal supports hyperlinks
if [ "$COLORTERM" = "xfce4-terminal" ]; then
return 0
fi
# Windows Terminal also supports hyperlinks
if [ -n "$WT_SESSION" ]; then
return 0
fi
# Konsole supports hyperlinks, but it's an opt-in setting that can't be detected
# https://github.com/ohmyzsh/ohmyzsh/issues/10964
# if [ -n "$KONSOLE_VERSION" ]; then
# return 0
# fi
return 1
}
#############################
# RELEASE CHANGELOG DISPLAY #
#############################
function display-release {
# This function uses the following globals: output, version,
# types (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
# - types, 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 (( ${+types[$rhash]} )); then
# Remove revert commit
unset "types[$hash]" "subjects[$hash]" "scopes[$hash]" "breaking[$hash]"
# Remove reverted commit
unset "types[$rhash]" "subjects[$rhash]" "scopes[$rhash]" "breaking[$rhash]"
fi
done
# Remove commits from ignored types unless it has breaking change information
for hash in ${(k)types[(R)${(j:|:)IGNORED_TYPES}]}; do
(( ! ${+breaking[$hash]} )) || continue
unset "types[$hash]" "subjects[$hash]" "scopes[$hash]"
done
# If no commits left skip displaying the release
if (( $#types == 0 )); then
return
fi
# Get length of longest scope for padding
local max_scope=0
for hash in ${(k)scopes}; do
max_scope=$(( max_scope < ${#scopes[$hash]} ? ${#scopes[$hash]} : max_scope ))
done
##* 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}"
local short_hash="${hash:0:7}" # 7 characters sha, top level sha is 12 characters
case "$output" in
raw) printf '%s' "$short_hash" ;;
text)
local text="\e[33m$short_hash\e[0m"; # red
if supports_hyperlinks; then
printf "\e]8;;%s\a%s\e]8;;\a" "https://github.com/ohmyzsh/ohmyzsh/commit/$hash" $text;
else
echo $text;
fi ;;
md) printf '[`%s`](https://github.com/ohmyzsh/ohmyzsh/commit/%s)' "$short_hash" "$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 '%s\n%s\n\n' "$header" "$(printf '%.0s=' {1..${#header}})" ;;
2) printf '%s\n%s\n\n' "$header" "$(printf '%.0s-' {1..${#header}})" ;;
*) printf '%s:\n\n' "$header" ;;
esac ;;
text)
case "$level" in
1|2) printf '\e[1;4m%s\e[0m\n\n' "$header" ;; # bold, underlined
*) printf '\e[1m%s:\e[0m\n\n' "$header" ;; # bold
esac ;;
md) printf '%s %s\n\n' "$(printf '%.0s#' {1..${level}})" "$header" ;;
esac
}
function fmt:scope {
#* Uses $scopes (A) and $hash from outer scope
local scope="${1:-${scopes[$hash]}}"
# If no scopes, exit the function
if [[ $max_scope -eq 0 ]]; then
return
fi
# Get how much padding is required for this scope
local padding=0
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 '[%s]%s ' "$scope" "$padding";;
text) printf '[\e[38;5;9m%s\e[0m]%s ' "$scope" "$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 '%s' "$subject" ;;
# In text mode, highlight (#<issue>) and dim text between `backticks`
text)
if supports_hyperlinks; then
sed -E $'s|#([0-9]+)|\e]8;;https://github.com/ohmyzsh/ohmyzsh/issues/\\1\a\e[32m#\\1\e[0m\e]8;;\a|g;s|`([^`]+)`|`\e[2m\\1\e[0m`|g' <<< "$subject"
else
sed -E $'s|#([0-9]+)|\e[32m#\\1\e[0m|g;s|`([^`]+)`|`\e[2m\\1\e[0m`|g' <<< "$subject"
fi ;;
# In markdown mode, link to (#<issue>) 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 '%s: ' "$type" ;;
text) printf '\e[4m%s\e[24m: ' "$type" ;; # underlined
esac
}
##* Section functions
function display:version {
fmt:header "$version" 2
}
function display:breaking {
(( $#breaking != 0 )) || return 0
case "$output" in
text) printf '\e[31m'; fmt:header "BREAKING CHANGES" 3 ;;
raw) fmt:header "BREAKING CHANGES" 3 ;;
md) fmt:header "BREAKING CHANGES ⚠" 3 ;;
esac
local hash message
local wrap_width=$(( (COLUMNS < 100 ? COLUMNS : 100) - 3 ))
for hash message in ${(kv)breaking}; do
# Format the BREAKING CHANGE message by word-wrapping it at maximum 100
# characters (use $COLUMNS if smaller than 100)
message="$(fmt -w $wrap_width <<< "$message")"
# Display hash and scope in their own line, and then the full message with
# blank lines as separators and a 3-space left padding
echo " - $(fmt:hash) $(fmt:scope)\n\n$(fmt:subject "$message" | sed 's/^/ /')\n"
done
}
function display:type {
local hash type="$1"
local -a hashes
hashes=(${(k)types[(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)types[(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
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 [[ -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
unset since
fi
# Commit classification arrays
local -A types subjects scopes breaking reverts
local truncate=0 read_commits=0
local version tag
local hash refs subject body
# Get the first version name:
# 1) try tag-like version, or
# 2) try branch name, or
# 3) try name-rev, or
# 4) try short hash
version=$(command git describe --tags $until 2>/dev/null) \
|| version=$(command git symbolic-ref --quiet --short $until 2>/dev/null) \
|| version=$(command git name-rev --no-undefined --name-only --exclude="remotes/*" $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
local range=${since:+$since..}$until
# Git log options
# -z: commits are delimited by null bytes
# --format: [7-char hash]<field sep>[ref names]<field sep>[subject]<field sep>[body]
# --abbrev=7: force commit hashes to be 12 characters long
# --no-merges: merge commits are omitted
# --first-parent: commits from merged branches are omitted
local SEP="0mZmAgIcSeP"
local -a raw_commits
raw_commits=(${(0)"$(command git -c log.showSignature=false log -z \
--format="%h${SEP}%D${SEP}%s${SEP}%b" --abbrev=12 \
--no-merges --first-parent $range)"})
local raw_commit
local -a raw_fields
for raw_commit in $raw_commits; do
# Truncate list on versions with a lot of commits
if [[ -z "$since" ]] && (( ++read_commits > 35 )); then
truncate=1
break
fi
# Read the commit fields (@ is needed to keep empty values)
eval "raw_fields=(\"\${(@ps:$SEP:)raw_commit}\")"
hash="${raw_fields[1]}"
refs="${raw_fields[2]}"
subject="${raw_fields[3]}"
body="${raw_fields[4]}"
# If we find a new release (exact tag)
if [[ "$refs" = *tag:\ * ]]; then
# Parse tag name (needs: setopt extendedglob)
tag="${${refs##*tag: }%%,# *}"
# Output previous release
display-release
# Reinitialize commit storage
types=()
subjects=()
scopes=()
breaking=()
reverts=()
# Start work on next release
version="$tag"
read_commits=1
fi
parse-commit "$hash" "$subject" "$body"
done
display-release
if (( truncate )); then
echo " ...more commits omitted"
echo
fi
}
# Use raw output if stdout is not a tty
if [[ ! -t 1 && -z "$3" ]]; then
main "$1" "$2" --raw
else
main "$@"
fi

View file

@ -0,0 +1,556 @@
#!/bin/sh
#
# This script should be run via curl:
# sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# or via wget:
# sh -c "$(wget -qO- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# or via fetch:
# sh -c "$(fetch -o - https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
#
# As an alternative, you can first download the install script and run it afterwards:
# wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
# sh install.sh
#
# You can tweak the install behavior by setting variables when running the script. For
# example, to change the path to the Oh My Zsh repository:
# ZSH=~/.zsh sh install.sh
#
# Respects the following environment variables:
# ZDOTDIR - path to Zsh dotfiles directory (default: unset). See [1][2]
# [1] https://zsh.sourceforge.io/Doc/Release/Parameters.html#index-ZDOTDIR
# [2] https://zsh.sourceforge.io/Doc/Release/Files.html#index-ZDOTDIR_002c-use-of
# ZSH - path to the Oh My Zsh repository folder (default: $HOME/.oh-my-zsh)
# REPO - name of the GitHub repo to install from (default: ohmyzsh/ohmyzsh)
# REMOTE - full remote URL of the git repo to install (default: GitHub via HTTPS)
# BRANCH - branch to check out immediately after install (default: master)
#
# Other options:
# CHSH - 'no' means the installer will not change the default shell (default: yes)
# RUNZSH - 'no' means the installer will not run zsh after the install (default: yes)
# KEEP_ZSHRC - 'yes' means the installer will not replace an existing .zshrc (default: no)
#
# You can also pass some arguments to the install script to set some these options:
# --skip-chsh: has the same behavior as setting CHSH to 'no'
# --unattended: sets both CHSH and RUNZSH to 'no'
# --keep-zshrc: sets KEEP_ZSHRC to 'yes'
# For example:
# sh install.sh --unattended
# or:
# sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
#
set -e
# Make sure important variables exist if not already defined
#
# $USER is defined by login(1) which is not always executed (e.g. containers)
# POSIX: https://pubs.opengroup.org/onlinepubs/009695299/utilities/id.html
USER=${USER:-$(id -u -n)}
# $HOME is defined at the time of login, but it could be unset. If it is unset,
# a tilde by itself (~) will not be expanded to the current user's home directory.
# POSIX: https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap08.html#tag_08_03
HOME="${HOME:-$(getent passwd $USER 2>/dev/null | cut -d: -f6)}"
# macOS does not have getent, but this works even if $HOME is unset
HOME="${HOME:-$(eval echo ~$USER)}"
# Track if $ZSH was provided
custom_zsh=${ZSH:+yes}
# Use $zdot to keep track of where the directory is for zsh dotfiles
# To check if $ZDOTDIR was provided, explicitly check for $ZDOTDIR
zdot="${ZDOTDIR:-$HOME}"
# Default value for $ZSH
# a) if $ZDOTDIR is supplied and not $HOME: $ZDOTDIR/ohmyzsh
# b) otherwise, $HOME/.oh-my-zsh
if [ -n "$ZDOTDIR" ] && [ "$ZDOTDIR" != "$HOME" ]; then
ZSH="${ZSH:-$ZDOTDIR/ohmyzsh}"
fi
ZSH="${ZSH:-$HOME/.oh-my-zsh}"
# Default settings
REPO=${REPO:-ohmyzsh/ohmyzsh}
REMOTE=${REMOTE:-https://github.com/${REPO}.git}
BRANCH=${BRANCH:-master}
# Other options
CHSH=${CHSH:-yes}
RUNZSH=${RUNZSH:-yes}
KEEP_ZSHRC=${KEEP_ZSHRC:-no}
command_exists() {
command -v "$@" >/dev/null 2>&1
}
user_can_sudo() {
# Check if sudo is installed
command_exists sudo || return 1
# Termux can't run sudo, so we can detect it and exit the function early.
case "$PREFIX" in
*com.termux*) return 1 ;;
esac
# The following command has 3 parts:
#
# 1. Run `sudo` with `-v`. Does the following:
# • with privilege: asks for a password immediately.
# • without privilege: exits with error code 1 and prints the message:
# Sorry, user <username> may not run sudo on <hostname>
#
# 2. Pass `-n` to `sudo` to tell it to not ask for a password. If the
# password is not required, the command will finish with exit code 0.
# If one is required, sudo will exit with error code 1 and print the
# message:
# sudo: a password is required
#
# 3. Check for the words "may not run sudo" in the output to really tell
# whether the user has privileges or not. For that we have to make sure
# to run `sudo` in the default locale (with `LANG=`) so that the message
# stays consistent regardless of the user's locale.
#
! LANG= sudo -n -v 2>&1 | grep -q "may not run sudo"
}
# The [ -t 1 ] check only works when the function is not called from
# a subshell (like in `$(...)` or `(...)`, so this hack redefines the
# function at the top level to always return false when stdout is not
# a tty.
if [ -t 1 ]; then
is_tty() {
true
}
else
is_tty() {
false
}
fi
# This function uses the logic from supports-hyperlinks[1][2], which is
# made by Kat Marchán (@zkat) and licensed under the Apache License 2.0.
# [1] https://github.com/zkat/supports-hyperlinks
# [2] https://crates.io/crates/supports-hyperlinks
#
# Copyright (c) 2021 Kat Marchán
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
supports_hyperlinks() {
# $FORCE_HYPERLINK must be set and be non-zero (this acts as a logic bypass)
if [ -n "$FORCE_HYPERLINK" ]; then
[ "$FORCE_HYPERLINK" != 0 ]
return $?
fi
# If stdout is not a tty, it doesn't support hyperlinks
is_tty || return 1
# DomTerm terminal emulator (domterm.org)
if [ -n "$DOMTERM" ]; then
return 0
fi
# VTE-based terminals above v0.50 (Gnome Terminal, Guake, ROXTerm, etc)
if [ -n "$VTE_VERSION" ]; then
[ $VTE_VERSION -ge 5000 ]
return $?
fi
# If $TERM_PROGRAM is set, these terminals support hyperlinks
case "$TERM_PROGRAM" in
Hyper|iTerm.app|terminology|WezTerm|vscode) return 0 ;;
esac
# These termcap entries support hyperlinks
case "$TERM" in
xterm-kitty|alacritty|alacritty-direct) return 0 ;;
esac
# xfce4-terminal supports hyperlinks
if [ "$COLORTERM" = "xfce4-terminal" ]; then
return 0
fi
# Windows Terminal also supports hyperlinks
if [ -n "$WT_SESSION" ]; then
return 0
fi
# Konsole supports hyperlinks, but it's an opt-in setting that can't be detected
# https://github.com/ohmyzsh/ohmyzsh/issues/10964
# if [ -n "$KONSOLE_VERSION" ]; then
# return 0
# fi
return 1
}
# Adapted from code and information by Anton Kochkov (@XVilka)
# Source: https://gist.github.com/XVilka/8346728
supports_truecolor() {
case "$COLORTERM" in
truecolor|24bit) return 0 ;;
esac
case "$TERM" in
iterm |\
tmux-truecolor |\
linux-truecolor |\
xterm-truecolor |\
screen-truecolor) return 0 ;;
esac
return 1
}
fmt_link() {
# $1: text, $2: url, $3: fallback mode
if supports_hyperlinks; then
printf '\033]8;;%s\033\\%s\033]8;;\033\\\n' "$2" "$1"
return
fi
case "$3" in
--text) printf '%s\n' "$1" ;;
--url|*) fmt_underline "$2" ;;
esac
}
fmt_underline() {
is_tty && printf '\033[4m%s\033[24m\n' "$*" || printf '%s\n' "$*"
}
# shellcheck disable=SC2016 # backtick in single-quote
fmt_code() {
is_tty && printf '`\033[2m%s\033[22m`\n' "$*" || printf '`%s`\n' "$*"
}
fmt_error() {
printf '%sError: %s%s\n' "${FMT_BOLD}${FMT_RED}" "$*" "$FMT_RESET" >&2
}
setup_color() {
# Only use colors if connected to a terminal
if ! is_tty; then
FMT_RAINBOW=""
FMT_RED=""
FMT_GREEN=""
FMT_YELLOW=""
FMT_BLUE=""
FMT_BOLD=""
FMT_RESET=""
return
fi
if supports_truecolor; then
FMT_RAINBOW="
$(printf '\033[38;2;255;0;0m')
$(printf '\033[38;2;255;97;0m')
$(printf '\033[38;2;247;255;0m')
$(printf '\033[38;2;0;255;30m')
$(printf '\033[38;2;77;0;255m')
$(printf '\033[38;2;168;0;255m')
$(printf '\033[38;2;245;0;172m')
"
else
FMT_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')
"
fi
FMT_RED=$(printf '\033[31m')
FMT_GREEN=$(printf '\033[32m')
FMT_YELLOW=$(printf '\033[33m')
FMT_BLUE=$(printf '\033[34m')
FMT_BOLD=$(printf '\033[1m')
FMT_RESET=$(printf '\033[0m')
}
setup_ohmyzsh() {
# Prevent the cloned repository from having insecure permissions. Failing to do
# so causes compinit() calls to fail with "command not found: compdef" errors
# for users with insecure umasks (e.g., "002", allowing group writability). Note
# that this will be ignored under Cygwin by default, as Windows ACLs take
# precedence over umasks except for filesystems mounted with option "noacl".
umask g-w,o-w
echo "${FMT_BLUE}Cloning Oh My Zsh...${FMT_RESET}"
command_exists git || {
fmt_error "git is not installed"
exit 1
}
ostype=$(uname)
if [ -z "${ostype%CYGWIN*}" ] && git --version | grep -Eq 'msysgit|windows'; then
fmt_error "Windows/MSYS Git is not supported on Cygwin"
fmt_error "Make sure the Cygwin git package is installed and is first on the \$PATH"
exit 1
fi
# Manual clone with git config options to support git < v1.7.2
git init --quiet "$ZSH" && cd "$ZSH" \
&& git config core.eol lf \
&& git config core.autocrlf false \
&& git config fsck.zeroPaddedFilemode ignore \
&& git config fetch.fsck.zeroPaddedFilemode ignore \
&& git config receive.fsck.zeroPaddedFilemode ignore \
&& git config oh-my-zsh.remote origin \
&& git config oh-my-zsh.branch "$BRANCH" \
&& git remote add origin "$REMOTE" \
&& git fetch --depth=1 origin \
&& git checkout -b "$BRANCH" "origin/$BRANCH" || {
[ ! -d "$ZSH" ] || {
cd -
rm -rf "$ZSH" 2>/dev/null
}
fmt_error "git clone of oh-my-zsh repo failed"
exit 1
}
# Exit installation directory
cd -
echo
}
setup_zshrc() {
# Keep most recent old .zshrc at .zshrc.pre-oh-my-zsh, and older ones
# with datestamp of installation that moved them aside, so we never actually
# destroy a user's original zshrc
echo "${FMT_BLUE}Looking for an existing zsh config...${FMT_RESET}"
# Must use this exact name so uninstall.sh can find it
OLD_ZSHRC="$zdot/.zshrc.pre-oh-my-zsh"
if [ -f "$zdot/.zshrc" ] || [ -h "$zdot/.zshrc" ]; then
# Skip this if the user doesn't want to replace an existing .zshrc
if [ "$KEEP_ZSHRC" = yes ]; then
echo "${FMT_YELLOW}Found ${zdot}/.zshrc.${FMT_RESET} ${FMT_GREEN}Keeping...${FMT_RESET}"
return
fi
if [ -e "$OLD_ZSHRC" ]; then
OLD_OLD_ZSHRC="${OLD_ZSHRC}-$(date +%Y-%m-%d_%H-%M-%S)"
if [ -e "$OLD_OLD_ZSHRC" ]; then
fmt_error "$OLD_OLD_ZSHRC exists. Can't back up ${OLD_ZSHRC}"
fmt_error "re-run the installer again in a couple of seconds"
exit 1
fi
mv "$OLD_ZSHRC" "${OLD_OLD_ZSHRC}"
echo "${FMT_YELLOW}Found old .zshrc.pre-oh-my-zsh." \
"${FMT_GREEN}Backing up to ${OLD_OLD_ZSHRC}${FMT_RESET}"
fi
echo "${FMT_YELLOW}Found ${zdot}/.zshrc.${FMT_RESET} ${FMT_GREEN}Backing up to ${OLD_ZSHRC}${FMT_RESET}"
mv "$zdot/.zshrc" "$OLD_ZSHRC"
fi
echo "${FMT_GREEN}Using the Oh My Zsh template file and adding it to $zdot/.zshrc.${FMT_RESET}"
# Modify $ZSH variable in .zshrc directory to use the literal $ZDOTDIR or $HOME
omz="$ZSH"
if [ -n "$ZDOTDIR" ] && [ "$ZDOTDIR" != "$HOME" ]; then
omz=$(echo "$omz" | sed "s|^$ZDOTDIR/|\$ZDOTDIR/|")
fi
omz=$(echo "$omz" | sed "s|^$HOME/|\$HOME/|")
sed "s|^export ZSH=.*$|export ZSH=\"${omz}\"|" "$ZSH/templates/zshrc.zsh-template" > "$zdot/.zshrc-omztemp"
mv -f "$zdot/.zshrc-omztemp" "$zdot/.zshrc"
echo
}
setup_shell() {
# Skip setup if the user wants or stdin is closed (not running interactively).
if [ "$CHSH" = no ]; then
return
fi
# If this user's login shell is already "zsh", do not attempt to switch.
if [ "$(basename -- "$SHELL")" = "zsh" ]; then
return
fi
# If this platform doesn't provide a "chsh" command, bail out.
if ! command_exists chsh; then
cat <<EOF
I can't change your shell automatically because this system does not have chsh.
${FMT_BLUE}Please manually change your default shell to zsh${FMT_RESET}
EOF
return
fi
echo "${FMT_BLUE}Time to change your default shell to zsh:${FMT_RESET}"
# Prompt for user choice on changing the default login shell
printf '%sDo you want to change your default shell to zsh? [Y/n]%s ' \
"$FMT_YELLOW" "$FMT_RESET"
read -r opt
case $opt in
y*|Y*|"") ;;
n*|N*) echo "Shell change skipped."; return ;;
*) echo "Invalid choice. Shell change skipped."; return ;;
esac
# Check if we're running on Termux
case "$PREFIX" in
*com.termux*) termux=true; zsh=zsh ;;
*) termux=false ;;
esac
if [ "$termux" != true ]; then
# Test for the right location of the "shells" file
if [ -f /etc/shells ]; then
shells_file=/etc/shells
elif [ -f /usr/share/defaults/etc/shells ]; then # Solus OS
shells_file=/usr/share/defaults/etc/shells
else
fmt_error "could not find /etc/shells file. Change your default shell manually."
return
fi
# Get the path to the right zsh binary
# 1. Use the most preceding one based on $PATH, then check that it's in the shells file
# 2. If that fails, get a zsh path from the shells file, then check it actually exists
if ! zsh=$(command -v zsh) || ! grep -qx "$zsh" "$shells_file"; then
if ! zsh=$(grep '^/.*/zsh$' "$shells_file" | tail -n 1) || [ ! -f "$zsh" ]; then
fmt_error "no zsh binary found or not present in '$shells_file'"
fmt_error "change your default shell manually."
return
fi
fi
fi
# We're going to change the default shell, so back up the current one
if [ -n "$SHELL" ]; then
echo "$SHELL" > "$zdot/.shell.pre-oh-my-zsh"
else
grep "^$USER:" /etc/passwd | awk -F: '{print $7}' > "$zdot/.shell.pre-oh-my-zsh"
fi
echo "Changing your shell to $zsh..."
# Check if user has sudo privileges to run `chsh` with or without `sudo`
#
# This allows the call to succeed without password on systems where the
# user does not have a password but does have sudo privileges, like in
# Google Cloud Shell.
#
# On systems that don't have a user with passwordless sudo, the user will
# be prompted for the password either way, so this shouldn't cause any issues.
#
if user_can_sudo; then
sudo -k chsh -s "$zsh" "$USER" # -k forces the password prompt
else
chsh -s "$zsh" "$USER" # run chsh normally
fi
# Check if the shell change was successful
if [ $? -ne 0 ]; then
fmt_error "chsh command unsuccessful. Change your default shell manually."
else
export SHELL="$zsh"
echo "${FMT_GREEN}Shell successfully changed to '$zsh'.${FMT_RESET}"
fi
echo
}
# shellcheck disable=SC2183 # printf string has more %s than arguments ($FMT_RAINBOW expands to multiple arguments)
print_success() {
printf '%s %s__ %s %s %s %s %s__ %s\n' $FMT_RAINBOW $FMT_RESET
printf '%s ____ %s/ /_ %s ____ ___ %s__ __ %s ____ %s_____%s/ /_ %s\n' $FMT_RAINBOW $FMT_RESET
printf '%s / __ \\%s/ __ \\ %s / __ `__ \\%s/ / / / %s /_ / %s/ ___/%s __ \\ %s\n' $FMT_RAINBOW $FMT_RESET
printf '%s/ /_/ /%s / / / %s / / / / / /%s /_/ / %s / /_%s(__ )%s / / / %s\n' $FMT_RAINBOW $FMT_RESET
printf '%s\\____/%s_/ /_/ %s /_/ /_/ /_/%s\\__, / %s /___/%s____/%s_/ /_/ %s\n' $FMT_RAINBOW $FMT_RESET
printf '%s %s %s %s /____/ %s %s %s %s....is now installed!%s\n' $FMT_RAINBOW $FMT_GREEN $FMT_RESET
printf '\n'
printf '\n'
printf "%s %s %s\n" "Before you scream ${FMT_BOLD}${FMT_YELLOW}Oh My Zsh!${FMT_RESET} look over the" \
"$(fmt_code "$(fmt_link ".zshrc" "file://$zdot/.zshrc" --text)")" \
"file to select plugins, themes, and options."
printf '\n'
printf '%s\n' "• Follow us on X: $(fmt_link @ohmyzsh https://x.com/ohmyzsh)"
printf '%s\n' "• Join our Discord community: $(fmt_link "Discord server" https://discord.gg/ohmyzsh)"
printf '%s\n' "• Get stickers, t-shirts, coffee mugs and more: $(fmt_link "Planet Argon Shop" https://shop.planetargon.com/collections/oh-my-zsh)"
printf '%s\n' $FMT_RESET
}
main() {
# Run as unattended if stdin is not a tty
if [ ! -t 0 ]; then
RUNZSH=no
CHSH=no
fi
# Parse arguments
while [ $# -gt 0 ]; do
case $1 in
--unattended) RUNZSH=no; CHSH=no ;;
--skip-chsh) CHSH=no ;;
--keep-zshrc) KEEP_ZSHRC=yes ;;
esac
shift
done
setup_color
if ! command_exists zsh; then
echo "${FMT_YELLOW}Zsh is not installed.${FMT_RESET} Please install zsh first."
exit 1
fi
if [ -d "$ZSH" ]; then
echo "${FMT_YELLOW}The \$ZSH folder already exists ($ZSH).${FMT_RESET}"
if [ "$custom_zsh" = yes ]; then
cat <<EOF
You ran the installer with the \$ZSH setting or the \$ZSH variable is
exported. You have 3 options:
1. Unset the ZSH variable when calling the installer:
$(fmt_code "ZSH= sh install.sh")
2. Install Oh My Zsh to a directory that doesn't exist yet:
$(fmt_code "ZSH=path/to/new/ohmyzsh/folder sh install.sh")
3. (Caution) If the folder doesn't contain important information,
you can just remove it with $(fmt_code "rm -r $ZSH")
EOF
else
echo "You'll need to remove it if you want to reinstall."
fi
exit 1
fi
# Create ZDOTDIR folder structure if it doesn't exist
if [ -n "$ZDOTDIR" ]; then
mkdir -p "$ZDOTDIR"
fi
setup_ohmyzsh
setup_zshrc
setup_shell
print_success
if [ $RUNZSH = no ]; then
echo "${FMT_YELLOW}Run zsh to try it out.${FMT_RESET}"
exit
fi
exec zsh -l
}
main "$@"

View file

@ -0,0 +1,161 @@
__require_tool_version_compare ()
{
(
# Locally ignore failures, otherwise we'll exit whenever $1 and $2
# are not equal!
set +e
awk_strverscmp='
# Use only awk features that work with 7th edition Unix awk (1978).
# My, what an old awk you have, Mr. Solaris!
END {
while (length(v1) || length(v2)) {
# Set d1 to be the next thing to compare from v1, and likewise for d2.
# Normally this is a single character, but if v1 and v2 contain digits,
# compare them as integers and fractions as strverscmp does.
if (v1 ~ /^[0-9]/ && v2 ~ /^[0-9]/) {
# Split v1 and v2 into their leading digit string components d1 and d2,
# and advance v1 and v2 past the leading digit strings.
for (len1 = 1; substr(v1, len1 + 1) ~ /^[0-9]/; len1++) continue
for (len2 = 1; substr(v2, len2 + 1) ~ /^[0-9]/; len2++) continue
d1 = substr(v1, 1, len1); v1 = substr(v1, len1 + 1)
d2 = substr(v2, 1, len2); v2 = substr(v2, len2 + 1)
if (d1 ~ /^0/) {
if (d2 ~ /^0/) {
# Compare two fractions.
while (d1 ~ /^0/ && d2 ~ /^0/) {
d1 = substr(d1, 2); len1--
d2 = substr(d2, 2); len2--
}
if (len1 != len2 && ! (len1 && len2 && substr(d1, 1, 1) == substr(d2, 1, 1))) {
# The two components differ in length, and the common prefix
# contains only leading zeros. Consider the longer to be less.
d1 = -len1
d2 = -len2
} else {
# Otherwise, compare as strings.
d1 = "x" d1
d2 = "x" d2
}
} else {
# A fraction is less than an integer.
exit 1
}
} else {
if (d2 ~ /^0/) {
# An integer is greater than a fraction.
exit 2
} else {
# Compare two integers.
d1 += 0
d2 += 0
}
}
} else {
# The normal case, without worrying about digits.
if (v1 == "") d1 = v1; else { d1 = substr(v1, 1, 1); v1 = substr(v1,2) }
if (v2 == "") d2 = v2; else { d2 = substr(v2, 1, 1); v2 = substr(v2,2) }
}
if (d1 < d2) exit 1
if (d1 > d2) exit 2
}
}
'
awk "$awk_strverscmp" v1="$1" v2="$2" /dev/null
case $? in
1) echo '<';;
0) echo '=';;
2) echo '>';;
esac
)
}
__require_tool_fatal ()
{
echo $@ >/dev/stderr
return 1
}
# Usage: require_tool program version
# Returns: 0 if $1 version if greater equals than $2, 1 otherwise.
# In case of error, message is written on error output.
#
# Example: require_tool gcc 4.6
# Use GCC environment variable if defined instead of lookup for the tool
# in the environment.
require_tool ()
{
envvar_name=$(echo $1 | tr '[:lower:]' '[:upper:]')
tool=$(printenv $envvar_name || echo $1)
local version=$($tool --version 2>/dev/null| \
sed -n 's/.*[^0-9.]\([0-9]*\.[0-9.]*\).*/\1/p;q')
if test x"$version" = x ; then
echo "$tool is required" >/dev/stderr
return 1
fi
case $(__require_tool_version_compare "$2" "$version") in
'>')
echo "$1 $2 or better is required: this is $tool $version" >/dev/stderr
return 1
;;
esac
}
usage() {
cat <<EOF
NAME
require_tool.sh - Ensure version of a tool is greater than the one expected
SYNOPSIS
require_tool.sh [ -h ]
[ --help ]
[ TOOL MIN_VERSION ]
DESCRIPTION
TOOL is the name or path of the program to check. If the name is specified, its
path is deduced from PATH environment variable. If environment variable TOOL
(in upper-case characters) is defined, considers its value as path to the tool.
MIN_VERSION is a string representing the minimum required version.
BEHAVIOR
* locate path to the program.
* execute $ TOOL_PATH --version
* extract version from standard output.
* compare this version to the expected one.
OPTIONS
-h --help
Display this message and exit 0
ERRORS
if program is not found or its version is prior to expected version,
a message is written to error output.
EXIT VALUE
returns 0 if program version if greater equals than expected version,
returns 1 otherwise.
EXAMPLE
$ require_tool.sh emacs 23
$ CC=g++ require_tool.sh cc 4.6
$ require_tool.sh zsh 4.5
EOF
}
for arg in $@; do
case $arg in
-h|--help)
usage
exit 0
;;
esac
done
if [ $# -gt 2 ] ; then
echo "ERROR: expecting 2 parameters. Please see option --help"
exit 1
fi
require_tool $@

View file

@ -0,0 +1,98 @@
#!/usr/bin/env zsh
# Zsh Theme Chooser by fox (fox91 at anche dot no)
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://www.wtfpl.net/txt/copying/ for more details.
THEMES_DIR="$ZSH/themes"
FAVLIST="${HOME}/.zsh_favlist"
source $ZSH/oh-my-zsh.sh
function noyes() {
read "a?$1 [y/N] "
if [[ $a == "N" || $a == "n" || $a = "" ]]; then
return 0
fi
return 1
}
function theme_preview() {
THEME=$1
THEME_NAME=`echo $THEME | sed s/\.zsh-theme$//`
print "$fg[blue]${(l.((${COLUMNS}-${#THEME_NAME}-5))..─.)}$reset_color $THEME_NAME $fg[blue]───$reset_color"
source "$THEMES_DIR/$THEME"
cols=$(tput cols)
(exit 1)
print -P "$PROMPT $RPROMPT"
}
function banner() {
echo
echo "╺━┓┏━┓╻ ╻ ╺┳╸╻ ╻┏━╸┏┳┓┏━╸ ┏━╸╻ ╻┏━┓┏━┓┏━┓┏━╸┏━┓"
echo "┏━┛┗━┓┣━┫ ┃ ┣━┫┣╸ ┃┃┃┣╸ ┃ ┣━┫┃ ┃┃ ┃┗━┓┣╸ ┣┳┛"
echo "┗━╸┗━┛╹ ╹ ╹ ╹ ╹┗━╸╹ ╹┗━╸ ┗━╸╹ ╹┗━┛┗━┛┗━┛┗━╸╹┗╸"
echo
}
function usage() {
echo "Usage: $0 [options] [theme]"
echo
echo "Options"
echo " -l List available themes"
echo " -s Show all themes"
echo " -h Get this help message"
exit 1
}
function list_themes() {
for THEME in $(ls $THEMES_DIR); do
THEME_NAME=`echo $THEME | sed s/\.zsh-theme$//`
echo $THEME_NAME
done
}
function insert_favlist() {
if grep -q "$THEME_NAME" $FAVLIST 2> /dev/null ; then
echo "Already in favlist"
else
echo $THEME_NAME >> $FAVLIST
echo "Saved to favlist"
fi
}
function theme_chooser() {
for THEME in $(ls $THEMES_DIR); do
echo
theme_preview $THEME
echo
if [[ -z $1 ]]; then
noyes "Do you want to add it to your favourite list ($FAVLIST)?" || \
insert_favlist $THEME_NAME
echo
fi
done
}
while getopts ":lhs" Option
do
case $Option in
l ) list_themes ;;
s ) theme_chooser 0 ;;
h ) usage ;;
* ) usage ;; # Default.
esac
done
if [[ -z $Option ]]; then
if [[ -z $1 ]]; then
banner
echo
theme_chooser
else
theme_preview $1".zsh-theme"
fi
fi

View file

@ -0,0 +1,295 @@
#!/usr/bin/env zsh
set +u # disable nounset
local ret=0 # exit code
# Protect against running with shells other than zsh
if [ -z "$ZSH_VERSION" ]; then
exec zsh "$0" "$@"
fi
# Protect against unwanted sourcing
case "$ZSH_EVAL_CONTEXT" in
*:file) echo "error: this file should not be sourced" && return 1 ;;
esac
# Define "$ZSH" if not defined -- in theory this should be `export`ed by the calling script
if [[ -z "$ZSH" ]]; then
ZSH="${0:a:h:h}"
fi
cd "$ZSH"
verbose_mode="default"
interactive=false
while getopts "v:i" opt; do
case $opt in
v)
if [[ $OPTARG == default || $OPTARG == minimal || $OPTARG == silent ]]; then
verbose_mode=$OPTARG
else
echo "[oh-my-zsh] update verbosity '$OPTARG' is not valid"
echo "[oh-my-zsh] valid options are 'default', 'minimal' and 'silent'"
fi
;;
i) interactive=true ;;
esac
done
# Use colors, but only if connected to a terminal
# and that terminal supports them.
# The [ -t 1 ] check only works when the function is not called from
# a subshell (like in `$(...)` or `(...)`, so this hack redefines the
# function at the top level to always return false when stdout is not
# a tty.
if [ -t 1 ]; then
is_tty() {
true
}
else
is_tty() {
false
}
fi
# This function uses the logic from supports-hyperlinks[1][2], which is
# made by Kat Marchán (@zkat) and licensed under the Apache License 2.0.
# [1] https://github.com/zkat/supports-hyperlinks
# [2] https://crates.io/crates/supports-hyperlinks
#
# Copyright (c) 2021 Kat Marchán
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
supports_hyperlinks() {
# $FORCE_HYPERLINK must be set and be non-zero (this acts as a logic bypass)
if [ -n "$FORCE_HYPERLINK" ]; then
[ "$FORCE_HYPERLINK" != 0 ]
return $?
fi
# If stdout is not a tty, it doesn't support hyperlinks
is_tty || return 1
# DomTerm terminal emulator (domterm.org)
if [ -n "$DOMTERM" ]; then
return 0
fi
# VTE-based terminals above v0.50 (Gnome Terminal, Guake, ROXTerm, etc)
if [ -n "$VTE_VERSION" ]; then
[ $VTE_VERSION -ge 5000 ]
return $?
fi
# If $TERM_PROGRAM is set, these terminals support hyperlinks
case "$TERM_PROGRAM" in
Hyper|iTerm.app|terminology|WezTerm|vscode) return 0 ;;
esac
# These termcap entries support hyperlinks
case "$TERM" in
xterm-kitty|alacritty|alacritty-direct) return 0 ;;
esac
# xfce4-terminal supports hyperlinks
if [ "$COLORTERM" = "xfce4-terminal" ]; then
return 0
fi
# Windows Terminal also supports hyperlinks
if [ -n "$WT_SESSION" ]; then
return 0
fi
# Konsole supports hyperlinks, but it's an opt-in setting that can't be detected
# https://github.com/ohmyzsh/ohmyzsh/issues/10964
# if [ -n "$KONSOLE_VERSION" ]; then
# return 0
# fi
return 1
}
# Adapted from code and information by Anton Kochkov (@XVilka)
# Source: https://gist.github.com/XVilka/8346728
supports_truecolor() {
case "$COLORTERM" in
truecolor|24bit) return 0 ;;
esac
case "$TERM" in
iterm |\
tmux-truecolor |\
linux-truecolor |\
xterm-truecolor |\
screen-truecolor) return 0 ;;
esac
return 1
}
fmt_link() {
# $1: text, $2: url, $3: fallback mode
if supports_hyperlinks; then
printf '\033]8;;%s\033\\%s\033]8;;\033\\\n' "$2" "$1"
return
fi
case "$3" in
--text) printf '%s\n' "$1" ;;
--url|*) fmt_underline "$2" ;;
esac
}
fmt_underline() {
is_tty && printf '\033[4m%s\033[24m\n' "$*" || printf '%s\n' "$*"
}
setopt typeset_silent
typeset -a RAINBOW
if is_tty; then
if supports_truecolor; then
RAINBOW=(
"$(printf '\033[38;2;255;0;0m')"
"$(printf '\033[38;2;255;97;0m')"
"$(printf '\033[38;2;247;255;0m')"
"$(printf '\033[38;2;0;255;30m')"
"$(printf '\033[38;2;77;0;255m')"
"$(printf '\033[38;2;168;0;255m')"
"$(printf '\033[38;2;245;0;172m')"
)
else
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')"
)
fi
RED=$(printf '\033[31m')
GREEN=$(printf '\033[32m')
YELLOW=$(printf '\033[33m')
BLUE=$(printf '\033[34m')
BOLD=$(printf '\033[1m')
RESET=$(printf '\033[0m')
fi
# Update upstream remote to ohmyzsh org
git remote -v | while read remote url extra; do
case "$url" in
git://github.com/robbyrussell/oh-my-zsh(|.git))
# Update out-of-date "unauthenticated git protocol on port 9418" to https
git remote set-url "$remote" "https://github.com/ohmyzsh/ohmyzsh.git" ;;
https://github.com/robbyrussell/oh-my-zsh(|.git))
git remote set-url "$remote" "https://github.com/ohmyzsh/ohmyzsh.git" ;;
git@github.com:robbyrussell/oh-my-zsh(|.git))
git remote set-url "$remote" "git@github.com:ohmyzsh/ohmyzsh.git" ;;
https://github.com/ohmyzsh/ohmyzsh(|.git)) ;;
git@github.com:ohmyzsh/ohmyzsh(|.git)) ;;
*) continue ;;
esac
# If we reach this point we have found the proper ohmyzsh upstream remote. If we don't,
# we'll only update from the set remote if `oh-my-zsh.remote` has been set to a remote,
# as when installing from a fork.
git config --local oh-my-zsh.remote "$remote"
break
done
# Set git-config values known to fix git errors
# Line endings (#4069)
git config core.eol lf
git config core.autocrlf false
# zeroPaddedFilemode fsck errors (#4963)
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>/dev/null)
git config rebase.autoStash true
local ret=0
# repository settings
remote=${"$(git config --local oh-my-zsh.remote)":-origin}
branch=${"$(git config --local oh-my-zsh.branch)":-master}
# repository state
last_head=$(git symbolic-ref --quiet --short HEAD || git rev-parse HEAD)
# checkout update branch
git checkout -q "$branch" -- || exit 1
# branch commit before update (used in changelog)
last_commit=$(git rev-parse "$branch")
# Update Oh My Zsh
if [[ $verbose_mode != silent ]]; then
printf "${BLUE}%s${RESET}\n" "Updating Oh My Zsh"
fi
if LANG= git pull --quiet --rebase $remote $branch; 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."
else
message="Hooray! Oh My Zsh has been updated!"
# Save the commit prior to updating
git config oh-my-zsh.lastVersion "$last_commit"
# Print changelog to the terminal
if [[ $interactive == true && $verbose_mode == default ]]; then
"$ZSH/tools/changelog.sh" HEAD "$last_commit"
fi
if [[ $verbose_mode != silent ]]; then
printf "${BLUE}%s \`${BOLD}%s${RESET}${BLUE}\`${RESET}\n" "You can see the changelog with" "omz changelog"
fi
fi
if [[ $verbose_mode == default ]]; then
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\n" "$message"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "To keep up with the latest news and updates, follow us on X:" "$(fmt_link @ohmyzsh https://x.com/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "Want to get involved in the community? Join our Discord:" "$(fmt_link "Discord server" https://discord.gg/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "Get your Oh My Zsh swag at:" "$(fmt_link "Planet Argon Shop" https://shop.planetargon.com/collections/oh-my-zsh)"
elif [[ $verbose_mode == minimal ]]; then
printf "${BLUE}%s${RESET}\n" "$message"
fi
else
ret=$?
printf "${RED}%s${RESET}\n" 'There was an error updating. Try again later?'
fi
# go back to HEAD previous to update
git checkout -q "$last_head" --
# Unset git-config values set just for the upgrade
case "$resetAutoStash" in
"") git config --unset rebase.autoStash ;;
*) git config rebase.autoStash "$resetAutoStash" ;;
esac
# Exit with `1` if the update failed
exit $ret

View file

@ -0,0 +1,41 @@
if hash chsh >/dev/null 2>&1 && [ -f ~/.shell.pre-oh-my-zsh ]; then
old_shell=$(cat ~/.shell.pre-oh-my-zsh)
echo "Switching your shell back to '$old_shell':"
if chsh -s "$old_shell"; then
rm -f ~/.shell.pre-oh-my-zsh
else
echo "Could not change default shell. Change it manually by running chsh"
echo "or editing the /etc/passwd file."
exit
fi
fi
read -r -p "Are you sure you want to remove Oh My Zsh? [y/N] " confirmation
if [ "$confirmation" != y ] && [ "$confirmation" != Y ]; then
echo "Uninstall cancelled"
exit
fi
echo "Removing ~/.oh-my-zsh"
if [ -d ~/.oh-my-zsh ]; then
rm -rf ~/.oh-my-zsh
fi
if [ -e ~/.zshrc ]; then
ZSHRC_SAVE=~/.zshrc.omz-uninstalled-$(date +%Y-%m-%d_%H-%M-%S)
echo "Found ~/.zshrc -- Renaming to ${ZSHRC_SAVE}"
mv ~/.zshrc "${ZSHRC_SAVE}"
fi
echo "Looking for original zsh config..."
ZSHRC_ORIG=~/.zshrc.pre-oh-my-zsh
if [ -e "$ZSHRC_ORIG" ]; then
echo "Found $ZSHRC_ORIG -- Restoring to ~/.zshrc"
mv "$ZSHRC_ORIG" ~/.zshrc
echo "Your original zsh config was restored."
else
echo "No original zsh config found"
fi
echo "Thanks for trying out Oh My Zsh. It's been uninstalled."
echo "Don't forget to restart your terminal!"