From 9281e358c562a5d5c0415f54af16cf1475dd5d1e Mon Sep 17 00:00:00 2001 From: Marc Henry de Frahan Date: Fri, 10 Apr 2026 11:45:59 -0600 Subject: [PATCH 1/4] Cleanup and unify shell scripts --- bin/executable_autotrim | 526 ------------------ bin/executable_chezmoi_shell_linter.sh | 229 ++++++++ ...ecutable_install_bootstrap_dependencies.sh | 111 ++-- bin/executable_install_emms-print-metadata.sh | 2 +- bin/executable_install_fonts.sh | 16 +- bin/executable_jpg2eps.sh | 31 +- bin/executable_keyremap.sh | 8 +- bin/executable_movie2gif.sh | 39 +- bin/executable_pdf2eps.sh | 28 +- bin/executable_pdf2epsv2.sh | 28 +- bin/executable_png2eps.sh | 54 +- bin/executable_pyenv_python_install.tmpl | 16 +- bin/executable_teatimer.sh | 19 +- docker/entrypoint-test.sh | 2 +- .../zinit/plugins/hpc/hpc.plugin.zsh.tmpl | 68 +-- .../plugins/personal/personal.plugin.zsh.tmpl | 48 +- .../executable_install.sh | 2 +- dot_zshrc.tmpl | 116 ++-- run_after_install-hunspell.sh | 4 +- run_after_install-packages-linux-post.sh.tmpl | 4 +- run_after_install-python-setup.sh.tmpl | 4 +- run_before_install-packages-linux.sh.tmpl | 2 +- 22 files changed, 527 insertions(+), 830 deletions(-) delete mode 100644 bin/executable_autotrim create mode 100644 bin/executable_chezmoi_shell_linter.sh diff --git a/bin/executable_autotrim b/bin/executable_autotrim deleted file mode 100644 index 11f9c19..0000000 --- a/bin/executable_autotrim +++ /dev/null @@ -1,526 +0,0 @@ -#!/bin/bash -# -# Developed by Fred Weinhaus 12/3/2007 .......... revised 4/30/2013 -# -# USAGE: autotrim [-f fuzzval] [-c coords] [-a angle] [-l left] [-r right ] [-t top ] [-b bottom ] infile outfile -# USAGE: autotrim [-h or -help] -# -# OPTIONS: -# -# -f fuzzval fuzz value for determining border color; -# expressed as (float) percent 0 to 100; -# default=0 (uniform color) -# -c coords pixel coordinate to extract color; may be -# expressed as gravity value (NorthWest, etc) -# or as "x,y" value; default is NorthWest=(0,0) -# -a angle angle of rotation of image; default indicates -# no rotation; angle=0 indicates to autocalculate; -# 0<=angle<=45 degrees (float) -# -l left pixel shift of left edge; +/- is right/left -# default=0 (no change) -# -r right pixel shift of right edge; +/- is right/left -# default=0 (no change) -# -t top pixel shift of top edge; +/- is down/up -# default=0 (no change) -# -b bottom pixel shift of bottom edge; +/- is down/up -# default=0 (no change) -# -h or -help get help -# -### -# -# NAME: AUTOTRIM -# -# PURPOSE: To trim the background border around a normally oriented or rotated image -# -# DESCRIPTION: AUTOTRIM automatically trims a (nearly) uniform color border -# around an image. If the image is rotated, one can trim to the bounding box -# around the image area or alternately trim to the maximum central area that -# contains no border pixels. The excess border does not have to completely -# surround the image. It may be only on one side. However, one must identify -# a coordinate within the border area for the algorithm to extract the base -# border color and also specify a fuzz value when the border color is not -# uniform. For simple border trimming of a normally oriented image or the -# bounding box of a rotated image, you may err somewhat towards larger than -# optimal fuzz values. For images that contain rotated picture data, when you -# want to trim to the central area, you should choose the smallest fuzz value -# that is appropriate. For images that contain rotated picture data, an -# estimate of the rotation angle is needed for the algorithm to work. However, -# setting the rotation angle to zero will let the algorithm determine the -# rotation angle. The resulting trim is usually pretty good for angles >= 5 -# degrees. If the result is off a little, you may use the left/right/top/bottom -# arguments to adjust the automatically determined trim region. -# -# -# Arguments: -# -# -h or -help --- displays help information. -# -# -f fuzzval --- FUZZVAL is the fuzz amount specified as a percent 0 to 100 -# (without the % sign). The default is zero which indicates that border is a -# uniform color. Larger values are needed when the border is not a uniform color. -# -# -c coords --- COORDS is any location within the border area for the -# algorithm to find the base border color. It may be specified in terms of -# gravity parameters (NorthWest, North, NorthEast, East, SouthEast, South, -# SouthWest or West) or as a pixel coordinate "x,y". The default is the -# upper left corner = NorthWest = "0,0". -# -# -a angle --- ANGLE is the rotation angle of the picture data within the image. -# The default (no argument) indicates that either the image does not contain -# rotated data or one simply wants to trim to the bounding box around the -# rotated data. If the angle is specified as zero, then the algorithm will -# automatically estimate the rotation angle which is needed when one wants -# to trim to the maximum central area of the rotated data which contains no -# border pixels. One may override the automatic determination and specify -# your own value. Values are positive floats between 0 and 45 degrees. The -# rotation direction is not important. Note that the algorithm cannot -# determine other orientations. You will need to do a 90, 180, or 270 degree -# rotation before or after using this script. -# -# -l left --- LEFT is the number of extra pixels to shift the trim of the left -# edge of the image. The trim is shifted right/left for +/- integer values. -# The default=0. -# -# -r right --- RIGHT is the number of extra pixels to shift the trim of the right -# edge of the image. The trim is shifted right/left for +/- integer values. -# The default=0. -# -# -t top --- TOP is the number of extra pixels to shift the trim of the top -# edge of the image. The trim is shifted down/up for +/- integer values. -# The default=0. -# -# -b bottom --- BOTTOM is the number of extra pixels to shift the trim of the -# bottom edge of the image. The trim is shifted down/up for +/- integer values. -# The default=0. -# -# CAVEAT: No guarantee that this script will work on all platforms, -# nor that trapping of inconsistent parameters is complete and -# foolproof. Use At Your Own Risk. -# -###### -# -# set default values; -fuzzval=0 # fuzz threshold -coords="NorthWest" # coordinates to get color -pad=1 # border pad size -rotang="" # rotation angle 0-45 or ""; 0 -- calc automatic; "" -- do not use -lt=0 # left edge shift of trim (+/- is right/left) -rt=0 # right edge shift of trim (+/- is right/left) -tp=0 # top edge shift of trim (+/- is down/up) -bm=0 # top bottom shift of trim (+/- is down/up) - -# set directory for temporary files -dir="." # suggestions are dir="." or dir="/tmp" - -# set up functions to report Usage and Usage with Description -PROGNAME=`type $0 | awk '{print $3}'` # search for executable on path -PROGDIR=`dirname $PROGNAME` # extract directory of program -PROGNAME=`basename $PROGNAME` # base name of program -usage1() - { - echo >&2 "" - echo >&2 "$PROGNAME:" "$@" - sed >&2 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" - } -usage2() - { - echo >&2 "" - echo >&2 "$PROGNAME:" "$@" - sed >&2 -n '/^######/q; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" - } - -# function to report error messages -errMsg() - { - echo "" - echo $1 - echo "" - usage1 - exit 1 - } - -# function to test for minus at start of value of second part of option 1 or 2 -checkMinus() - { - test=`echo "$1" | grep -c '^-.*$'` # returns 1 if match; 0 otherwise - [ $test -eq 1 ] && errMsg "$errorMsg" - } - -# test for correct number of arguments and get values -if [ $# -eq 0 ] - then - # help information - echo "" - usage2 - exit 0 -elif [ $# -eq 3 -o $# -eq 5 -o $# -eq 7 -o $# -eq 9 -o $# -eq 11 -o $# -eq 13 -o $# -eq 15 -o $# -gt 16 ] - then - errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---" -else - while [ $# -gt 0 ] - do - # get parameters - case "$1" in - -h|-help) # help information - echo "" - usage2 - ;; - -f) # fuzzval - shift # to get the next parameter - fuzzval - # test if parameter starts with minus sign - errorMsg="--- INVALID FUZZVAL SPECIFICATION ---" - checkMinus "$1" - fuzzval=`expr "$1" : '\([.0-9]*\)'` - [ "$fuzzval" = "" ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE FLOATING POINT VALUE (with no sign) ---" - fuzzvaltest=`echo "$fuzzval < 0" | bc` - [ $fuzzvaltest -eq 1 ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE FLOATING POINT VALUE ---" - ;; - -c) # coords - shift # to get the next parameter - coords - # test if parameter starts with minus sign - errorMsg="--- INVALID COORDS SPECIFICATION ---" - checkMinus "$1" - coords=$1 - # further testing done later - ;; - -a) # angle - shift # to get the next parameter - angle - # test if parameter starts with minus sign - errorMsg="--- INVALID ANGLE SPECIFICATION ---" - checkMinus "$1" - rotang=`expr "$1" : '\([.0-9]*\)'` - [ "$rotang" = "" ] && errMsg "--- ANGLE=$rotang MUST BE A NON-NEGATIVE FLOATING POINT VALUE (with no sign) ---" - rotangtestA=`echo "$rotang < 0" | bc` - rotangtestB=`echo "$rotang > 45" | bc` - [ $rotangtestA -eq 1 -a $rotangtestB -eq 1 ] && errMsg "--- ANGLE=$rotang MUST BE A NON-NEGATIVE FLOATING POINT VALUE LESS THAN OR EQUAL TO 45 ---" - ;; - -l) # left - shift # to get the next parameter - left - lt=`expr "$1" : '\([0-9\-]*\)'` - [ "$lt" = "" ] && errMsg "--- LEFT=$lt MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" - ;; - -r) # right - shift # to get the next parameter - right - rt=`expr "$1" : '\([0-9\-]*\)'` - [ "$rt" = "" ] && errMsg "--- RIGHT=$rt MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" - ;; - -t) # top - shift # to get the next parameter - top - tp=`expr "$1" : '\([0-9\-]*\)'` - [ "$tp" = "" ] && errMsg "--- TOP=$tp MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" - ;; - -b) # bottom - shift # to get the next parameter - bottom - bm=`expr "$1" : '\([0-9\-]*\)'` - [ "$bm" = "" ] && errMsg "--- BOTTOM=$bm MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" - ;; - -) # STDIN and end of arguments - break - ;; - -*) # any other - argument - errMsg "--- UNKNOWN OPTION ---" - ;; - *) # end of arguments - break - ;; - esac - shift # next option - done - # - # get infile and outfile - infile=$1 - outfile=$2 -fi - -# test that infile provided -[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED" - -# test that outfile provided -[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED" - - -# setup temporary images and auto delete upon exit -# use mpc/cache to hold input image temporarily in memory -# bug in IM 6.8.4.3 - 6.8.4.7 with miff -tmpA="$dir/autotrim_$$.mpc" -tmpB="$dir/autotrim_$$.cache" -tmp00="$dir/autotrim_00_$$.mpc" -tmp00a="$dir/autotrim_00_$$.cache" -tmp0="$dir/autotrim_0_$$.mpc" -tmp0a="$dir/autotrim_0_$$.cache" -tmp1="$dir/autotrim_1_$$.mpc" -tmp1a="$dir/autotrim_1_$$.cache" -tmp2="$dir/autotrim_2_$$.mpc" -tmp2a="$dir/autotrim_2_$$.cache" -tmp3="$dir/autotrim_3_$$.mpc" -tmp3a="$dir/autotrim_3_$$.cache" -trap "rm -f $tmpA $tmpB $tmp00 $tmp00a $tmp0 $tmp0a $tmp1 $tmp1a $tmp2 $tmp2a $tmp3 $tmp3a; exit 0" 0 -trap "rm -f $tmpA $tmpB $tmp00 $tmp00a $tmp0 $tmp0a $tmp1 $tmp1a $tmp2 $tmp2a $tmp3 $tmp3a; exit 1" 1 2 3 15 - - -if convert -quiet -regard-warnings "$infile" +repage "$tmpA" - then - : ' do nothing ' -else - errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" -fi - -# setup setcspace -# since do not know exact versions (see below) use simple convert test -test=`convert xc:black -set colorspace RGB txt:- | tail -n 1 | sed -n 's/^[0-9]*,[0-9]*: [(].*[)] #.* \(.*\)$/\1/p'` -if [ "$test" != "black" ]; then - setcspace="-set colorspace sRGB" -else - setcspace="" -fi - - - -# function to get dimensions -dimensions() - { - width=`identify -format %w $1` - height=`identify -format %h $1` - widthm1=`expr $width - 1` - heightm1=`expr $height - 1` - midwidth=`echo "scale=0; $width / 2" | bc` - midheight=`echo "scale=0; $height / 2" | bc` - widthmp=`expr $width - 2 \* $pad` - heightmp=`expr $height - 2 \* $pad` - } - -# function to get color at user specified location -getColor() - { - dimensions $tmpA - case "$coords" in - NorthWest|Northwest|northwest) coords="0,0" - ;; - North|north) coords="$midwidth,0" - ;; - NorthEast|Northeast|northeast) coords="$widthm1,0" - ;; - East|east) coords="$widthm1,$midheight" - ;; - SouthEast|Southeast|southeast) coords="$widthm1,$heightm1" - ;; - South|south) coords="$midwidth,$heightm1" - ;; - SouthWest|Southwest|southwest) coords="0,$heightm1" - ;; - West|west) coords="0,$midheight" - ;; - [0-9]*,[0-9]*) coords=$coords - ;; - *) errMsg "--- INVALID COORDS ---" - ;; - esac - color=`convert $tmpA -format "%[pixel:u.p{$coords}]" info:` - } - -# function to pad and extract binary image -paddedBinary() - { - # reset coords to 0,0 - coords="0,0" - - # pad image with border of color found at original coords - convert $tmpA -bordercolor $color -border ${pad}x${pad} $tmpA - - # get dimensions of padded image - dimensions $tmpA - - # make exterior transparent and inside white - convert $tmpA -fuzz $fuzzval% -fill none \ - -draw "matte $coords floodfill" -fill white +opaque none $tmp0 - - # make exterior black and inside white - convert \( -size ${width}x${height} xc:black \) $tmp0 -compose over -composite $tmp1 -# convert $tmp0 -background black -compose over -flatten $tmp1 - - } - -# function to get black to white transition location along row or column -# specify arguments 1D image of data, dimension=width,height,widthm1 or heightm1, and direction=inc or dec -getTransition() - { - img1D=$1 - dim=$2 - dir=$3 - rowcol=`convert $img1D -compress None -depth 8 txt:-` - vals=`echo "$rowcol" | sed -n 's/^[0-9]*,[0-9]*: [(].*[)] #...... \(.*\)$/\1/p'` - vals_Array=($vals) - if [ "$dir" = "inc" ] - then - i=0 - while [ $i -lt $dim ] - do - [ "${vals_Array[$i]}" = "white" ] && break - i=`expr $i + 1` - done - location=$i - elif [ "$dir" = "dec" ] - then - i=$dim - while [ $i -ge 0 ] - do - [ "${vals_Array[$i]}" = "white" ] && break - i=`expr $i - 1` - done - location=$i - fi - } - -# function to process binary to get cropped image -cropImage() - { - trim=$1 - angle=$2 - if [ "$angle" = "" ] - then - thresh=1 - normalize="" - elif [ `echo "($angle >= 0) && ($angle <= 45)" | bc` -eq 1 ] - then - # threshold relation to angle determined empirically and seems to be reasonably good, - # but not perfect probably due to quantization of boundary in binarized image - if [ `echo "$angle <= 5" | bc` -eq 1 ] - then - thresh=`echo "scale=1; (99 - (1.0 * $angle)) / 1" | bc` - else - thresh=`echo "scale=1; (99 - (1.07 * $angle)) / 1" | bc` - fi - thresh=$thresh% -# normalize="-normalize" - normalize="-contrast-stretch 0" - - else - errMsg "--- INVALID ANGLE VALUE ---" - fi - - # average to one row and one column -# convert $tmp1 -filter box -resize 1x${height}! $normalize -threshold $thresh $tmp2 -# convert $tmp1 -filter box -resize ${width}x1! $normalize -threshold $thresh $tmp3 - - # need to add -set colorspace RGB for versions after 6.7.7.7 to convert to nonlinear gray so thresholds correctly - # some versions after 6.6.9.10 and before 6.7.2.10 (tested at 6.7.2.0 and 6.7.2.1) produce srgb(...) values so must trap out -set colorspace sRGB before the output so they report black and white - # with the above scheme a few odd versions will show some white in the corners (e.g. tests show that in 6.7.7.8, 6.7.7.10, 6.7.8.2, 6.7.8.3, 6.8.4.0, 6.8.4.2, but not before 6.7.7.8 or 6.8.1.9, 6.8.3.9, 6.8.4.3 and after) - convert $tmp1 -scale 1x${height}! $normalize -set colorspace RGB -threshold $thresh $setcspace $tmp2 - convert $tmp1 -scale ${width}x1! $normalize -set colorspace RGB -threshold $thresh $setcspace $tmp3 - - # get top and bottom by locating first occurence of value=white from top and bottom of column - getTransition $tmp2 $height "inc" - top=$location - - getTransition $tmp2 $heightm1 "dec" - bottom=$location - - # get left and right by locating first occurence of value=white from left and right of row - getTransition $tmp3 $width "inc" - left=$location - - getTransition $tmp3 $widthm1 "dec" - right=$location - - #compute start x and y and width and height - if [ "$trim" = "" ] - then - new_x=$left - new_y=$top - new_width=`expr $right - $left + 1` - new_height=`expr $bottom - $top + 1` - else - new_x=`expr $left + $lt` - new_y=`expr $top + $tp` - new_width=`expr $right - $left - $lt + $rt + 1` - new_height=`expr $bottom - $top - $tp + $bm + 1` - fi - -#echo "top=$top; bottom=$bottom; left=$left; right=$right;" -#echo "new_x=$new_x; new_y=$new_y; new_width=$new_width; new_height=$new_height;" - - #crop image - convert $tmpA[${new_width}x${new_height}+${new_x}+${new_y}] +repage $tmpA - } - -# function to compute rotation angle -computeRotation() - { - # start with image already cropped to outside bounds of rotated image - - # get new dimension - dimensions $tmpA - - # get padded bindary image - paddedBinary - - # trim off pad (repage to clear page offsets) - convert $tmp1[${widthmp}x${heightmp}+1+1] +repage $tmp1 - - # get rotation angle - # get coord of 1st white pixel in left column - getTransition $tmp1[1x${height}+0+0] $height "inc" - p1x=1 - p1y=$location - - # get coord of 1st white pixel in top row - getTransition $tmp1[${width}x1+0+0] $width "inc" - p2x=$location - p2y=1 - - # compute slope and angle (reverse sign of dely as y increases downward) - delx=`expr $p2x - $p1x` - dely=`expr $p1y - $p2y` - if [ $delx -eq 0 ] - then - rotang=2 - else - pi=`echo "scale=10; 4*a(1)" | bc -l` - angle=`echo "scale=5; (180/$pi) * a($dely / $delx)" | bc -l` - if [ `echo "$angle > 45" | bc` -eq 1 ] - then - rotang=`echo "scale=2; (90 - $angle) / 1" | bc` - else - rotang=`echo "scale=2; $angle / 1" | bc` - fi - fi - echo "" - echo "Rotation Angle=$rotang" - echo "" - } - - -# start processing - -# get color at user specified location -getColor - -if [ "$rotang" = "" ] - then - # do processing to get simple bounding box - # crop out any border to get bounding box - paddedBinary $fuzzval - # process to trim (no rotation) - cropImage "trim" "" - -else - # alternately do processing to get inscribed, non-padded image area of rotated image - # crop out any border to get bounding box - paddedBinary $fuzzval - cropImage "trim" "" - # get rotation angle if appropriate - if [ `echo "$rotang == 0" | bc` -eq 1 ] - then - computeRotation - fi - # process to trim according to rotation angle - paddedBinary - cropImage "" $rotang - - # repeat second time (needed for non-square "true/unrotated" image area) - # seredipidously found that it seemed to work - # process to trim according to rotation angle - paddedBinary - cropImage "trim" $rotang -fi -convert $tmpA $outfile diff --git a/bin/executable_chezmoi_shell_linter.sh b/bin/executable_chezmoi_shell_linter.sh new file mode 100644 index 0000000..1e71376 --- /dev/null +++ b/bin/executable_chezmoi_shell_linter.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +errors=0 + +log_error() { + echo "ERROR: $1" + ((errors++)) || true +} + +log_warn() { + echo "WARN: $1" +} + +log_ok() { + echo "OK: $1" +} + +# Find all bash scripts (by shebang) +find_bash_scripts() { + find "${REPO_ROOT}" -type f ! -path '*/.git/*' -exec grep -l '^#!.*bash' {} \; 2>/dev/null +} + +# Find all zsh scripts (by name pattern, excluding p10k) +find_zsh_scripts() { + find "${REPO_ROOT}" -type f ! -path '*/.git/*' \( -name "*.zsh" -o -name "*.zsh.tmpl" -o -name "*zshrc*" \) ! -name "*p10k*" 2>/dev/null +} + +# Find all scripts (bash + zsh) +find_scripts() { + find_bash_scripts + find_zsh_scripts +} + +# All bash shebangs should be #!/usr/bin/env bash +check_shebangs() { + echo "=== Checking shebangs ===" + local bad_shebangs=0 + + while IFS= read -r file; do + # Find the shebang line (may be after chezmoi conditional on line 1-3) + shebang=$(head -5 "$file" | grep '^#!/' | head -1) + + if [[ "$shebang" != "#!/usr/bin/env bash" ]]; then + log_error "$file: non-standard shebang: $shebang" + ((bad_shebangs++)) || true + fi + done < <(find_bash_scripts) + + if [[ $bad_shebangs -eq 0 ]]; then + log_ok "All shebangs are #!/usr/bin/env bash" + fi +} + +# No backticks in code (excluding comments) +check_backticks() { + echo "" + echo "=== Checking for backticks ===" + local found=0 + local bt='`' + + # Check both bash and zsh scripts + while IFS= read -r file; do + # Skip this linter script (contains backtick literal for searching) + [[ "$file" == *"chezmoi_shell_linter"* ]] && continue + + # Check for backticks not in comments (handle whitespace before #) + if grep -n "${bt}" "$file" | grep -v '^[^:]*:[[:space:]]*#' | grep -q "${bt}"; then + matches=$(grep -n "${bt}" "$file" | grep -v '^[^:]*:[[:space:]]*#' || true) + if [[ -n "$matches" ]]; then + log_error "$file: contains backticks (use \$() instead)" + echo "$matches" | head -3 + ((found++)) || true + fi + fi + done < <(find_scripts) + + if [[ $found -eq 0 ]]; then + log_ok "No backticks found" + fi +} + +# Run shellcheck on all bash scripts (renders through chezmoi first) +check_shellcheck() { + echo "" + echo "=== Running shellcheck ===" + + if ! command -v shellcheck &>/dev/null; then + log_warn "shellcheck not installed, skipping" + return + fi + + if ! command -v chezmoi &>/dev/null; then + log_warn "chezmoi not installed, skipping" + return + fi + + local checked=0 + local failed=0 + local skipped=0 + + while IFS= read -r file; do + # Render through chezmoi (works for both .tmpl and plain files) + rendered=$(chezmoi execute-template <"$file" 2>/dev/null || true) + + # Skip if empty (OS-conditional template on wrong OS) + if [[ -z "$rendered" ]] || [[ "$rendered" =~ ^[[:space:]]*$ ]]; then + ((skipped++)) || true + continue + fi + + ((checked++)) || true + if ! echo "$rendered" | shellcheck --severity=style -s bash - 2>&1; then + log_error "shellcheck failed: $file" + ((failed++)) || true + fi + done < <(find_bash_scripts) + + if [[ $failed -eq 0 ]]; then + log_ok "shellcheck passed on $checked files ($skipped skipped)" + else + log_error "shellcheck failed on $failed/$checked files" + fi +} + +# Run shfmt on all scripts (renders through chezmoi first) +check_shfmt() { + echo "" + echo "=== Running shfmt ===" + + if ! command -v shfmt &>/dev/null; then + log_warn "shfmt not installed, skipping" + return + fi + + if ! command -v chezmoi &>/dev/null; then + log_warn "chezmoi not installed, skipping" + return + fi + + # Check if shfmt supports zsh + local shfmt_has_zsh=false + if echo "" | shfmt -ln=zsh -d 2>/dev/null; then + shfmt_has_zsh=true + fi + + local checked=0 + local failed=0 + local skipped=0 + + # Build list of zsh files for lookup + local -A zsh_files + while IFS= read -r file; do + zsh_files["$file"]=1 + done < <(find_zsh_scripts) + + # Check all scripts (bash + zsh) + while IFS= read -r file; do + # Determine language + local lang="bash" + if [[ -n "${zsh_files[$file]:-}" ]]; then + lang="zsh" + fi + + # Skip zsh if shfmt doesn't support it + if [[ "$lang" == "zsh" ]] && [[ "$shfmt_has_zsh" == "false" ]]; then + ((skipped++)) || true + continue + fi + + # Render through chezmoi (works for both .tmpl and plain files) + rendered=$(chezmoi execute-template <"$file" 2>/dev/null || true) + + # Skip if empty (OS-conditional template on wrong OS) + if [[ -z "$rendered" ]] || [[ "$rendered" =~ ^[[:space:]]*$ ]]; then + ((skipped++)) || true + continue + fi + + ((checked++)) || true + if ! echo "$rendered" | shfmt -ln="$lang" -i 4 -d 2>&1; then + log_error "shfmt ($lang) found issues: $file" + ((failed++)) || true + fi + done < <(find_scripts) + + local msg="shfmt passed on $checked files ($skipped skipped)" + if [[ "$shfmt_has_zsh" == "false" ]]; then + msg="$msg - zsh not supported by this shfmt version" + fi + + if [[ $failed -eq 0 ]]; then + log_ok "$msg" + else + log_error "shfmt found formatting issues in $failed/$checked files" + fi +} + +# Main +main() { + echo "Linting shell scripts in ${REPO_ROOT}" + echo "" + echo "Bash scripts:" + find_bash_scripts | sort | sed 's|^| |' + echo "" + echo "Zsh scripts:" + find_zsh_scripts | sort | sed 's|^| |' + echo "" + + check_shebangs + check_backticks + check_shellcheck + check_shfmt + + echo "" + if [[ $errors -gt 0 ]]; then + echo "Linting failed with $errors error(s)" + exit 1 + else + echo "Linting passed" + exit 0 + fi +} + +main "$@" diff --git a/bin/executable_install_bootstrap_dependencies.sh b/bin/executable_install_bootstrap_dependencies.sh index 46e4a39..3d4b815 100644 --- a/bin/executable_install_bootstrap_dependencies.sh +++ b/bin/executable_install_bootstrap_dependencies.sh @@ -2,65 +2,66 @@ OS="$(uname -s)" -if [[ "$OS" == "Darwin" ]]; then +if [[ "${OS}" == "Darwin" ]]; then brew install zsh chezmoi zoxide -elif [[ "$OS" == "Linux" ]]; then +elif [[ "${OS}" == "Linux" ]]; then if [[ -f /etc/os-release ]]; then + # shellcheck source=/dev/null . /etc/os-release - if [[ "$ID" == "ubuntu" ]]; then + if [[ "${ID}" == "ubuntu" ]]; then sudo apt-get update sudo apt install -y \ - git \ - zsh \ - curl \ - zoxide \ - make \ - wget \ - unzip \ - llvm \ - clang \ - clangd \ - clang-format \ - clang-tidy \ - cmake \ - emacs \ - build-essential \ - libbz2-dev \ - libreadline-dev \ - liblzma-dev \ - libsqlite3-dev \ - libssl-dev \ - zlib1g-dev \ - libncurses5-dev \ - libncursesw5-dev \ - libgdbm-dev \ - libnss3-dev \ - libtag1-dev \ - libtagc0-dev \ - xz-utils \ - tk-dev \ - libffi-dev \ - npm \ - tmux \ - file \ - ripgrep \ - ccls \ - mp3info \ - mpv \ - pianobar \ - shellcheck \ - vlc \ - vorbis-tools \ - hunspell \ - faac \ - grep \ - bc \ - gawk \ - coreutils \ - uuid-dev \ - less + git \ + zsh \ + curl \ + zoxide \ + make \ + wget \ + unzip \ + llvm \ + clang \ + clangd \ + clang-format \ + clang-tidy \ + cmake \ + emacs \ + build-essential \ + libbz2-dev \ + libreadline-dev \ + liblzma-dev \ + libsqlite3-dev \ + libssl-dev \ + zlib1g-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libgdbm-dev \ + libnss3-dev \ + libtag1-dev \ + libtagc0-dev \ + xz-utils \ + tk-dev \ + libffi-dev \ + npm \ + tmux \ + file \ + ripgrep \ + ccls \ + mp3info \ + mpv \ + pianobar \ + shellcheck \ + vlc \ + vorbis-tools \ + hunspell \ + faac \ + grep \ + bc \ + gawk \ + coreutils \ + uuid-dev \ + less else - echo "Linux (Not Ubuntu: $NAME)" + echo "Linux (Not Ubuntu: ${NAME})" fi else echo "Linux (Unknown distro)" @@ -69,7 +70,7 @@ elif [[ "$OS" == "Linux" ]]; then # Install chezmoi mkdir -p "${HOME}/.local/bin" sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "${HOME}/.local/bin" - export PATH="${HOME}/.local/bin:$PATH" + export PATH="${HOME}/.local/bin:${PATH}" else - echo "Unknown OS: $OS" + echo "Unknown OS: ${OS}" fi diff --git a/bin/executable_install_emms-print-metadata.sh b/bin/executable_install_emms-print-metadata.sh index cda7c18..af73f9e 100644 --- a/bin/executable_install_emms-print-metadata.sh +++ b/bin/executable_install_emms-print-metadata.sh @@ -30,7 +30,7 @@ else git clone --quiet https://git.savannah.gnu.org/git/emms.git "${EMMS_DIR}" fi -cd "${EMMS_DIR}/src" +cd "${EMMS_DIR}/src" || exit 1 if cc ${TAGLIB_INCLUDE} emms-print-metadata.c -ltag_c -ltag -lz -o "${EMMS_EXEC}" 2>/dev/null; then echo "Installed emms-print-metadata to ${EMMS_EXEC}" fi diff --git a/bin/executable_install_fonts.sh b/bin/executable_install_fonts.sh index def6141..a1f64f6 100644 --- a/bin/executable_install_fonts.sh +++ b/bin/executable_install_fonts.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash declare -a fonts=( FiraCode @@ -9,21 +9,21 @@ declare -a fonts=( version='3.4.0' fonts_dir="${HOME}/.local/share/fonts" -if [[ -f "$fonts_dir/FiraCodeNerdFont-Regular.ttf" ]]; then +if [[ -f "${fonts_dir}/FiraCodeNerdFont-Regular.ttf" ]]; then exit 0 fi -mkdir -p "$fonts_dir" +mkdir -p "${fonts_dir}" for font in "${fonts[@]}"; do zip_file="${font}.zip" download_url="https://github.com/ryanoasis/nerd-fonts/releases/download/v${version}/${zip_file}" - echo "Downloading $download_url" - wget "$download_url" - unzip -o "$zip_file" -d "$fonts_dir" - rm "$zip_file" + echo "Downloading ${download_url}" + wget "${download_url}" + unzip -o "${zip_file}" -d "${fonts_dir}" + rm "${zip_file}" done -find "$fonts_dir" -name '*Windows Compatible*' -delete +find "${fonts_dir}" -name '*Windows Compatible*' -delete fc-cache -fv diff --git a/bin/executable_jpg2eps.sh b/bin/executable_jpg2eps.sh index d4e65d0..695a162 100644 --- a/bin/executable_jpg2eps.sh +++ b/bin/executable_jpg2eps.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash # -# # -function myHelp () { +# +function myHelp() { cat <<-END Usage: ------ @@ -14,17 +14,14 @@ END } case "$1" in - -h | --help) - myHelp - exit - ;; - *) - echo "Converting $1 to an eps file." - file=$1 - convert "$file" eps2:$(echo "$file" | sed 's/\.jpg$/\.eps/') - shift - ;; -esac - - - +-h | --help) + myHelp + exit + ;; +*) + echo "Converting $1 to an eps file." + file=$1 + convert "$file" "eps2:${file%.jpg}.eps" + shift + ;; +esac diff --git a/bin/executable_keyremap.sh b/bin/executable_keyremap.sh index beb4f5c..f81a4c1 100644 --- a/bin/executable_keyremap.sh +++ b/bin/executable_keyremap.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Key remappings # @@ -18,15 +18,15 @@ action=$1 # Set productid variable, as per example below, to remap only specific # keyboard, or leave empty to remap all connected keyboards -productid_external='{"ProductID":0x23a}' -productid_builtin='{"ProductID":0x340}' +# productid_external='{"ProductID":0x23a}' +# productid_builtin='{"ProductID":0x340}' # productid="-m \'${productid_builtin}\'" productid="" if [[ ${action} =~ ^(e|en|ena|ena.*)$ ]]; then # 1. map right command (aka Right GUI) to Application (aka menu key) # 2. map caps lock to left control - hidutil property ${productid} --set '{"UserKeyMapping":[ + hidutil property "${productid}" --set '{"UserKeyMapping":[ { "HIDKeyboardModifierMappingSrc": 0x7000000E7, "HIDKeyboardModifierMappingDst": 0x700000065 diff --git a/bin/executable_movie2gif.sh b/bin/executable_movie2gif.sh index c5b49c6..e1eeb16 100644 --- a/bin/executable_movie2gif.sh +++ b/bin/executable_movie2gif.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash # -# # -function myHelp () { +# +function myHelp() { cat <<-END Usage: ------ @@ -15,22 +15,19 @@ END } case "$1" in - -h | --help) - myHelp - exit - ;; - *) - echo "Converting $1 to an gif file." - MYTEMPDIR=$(mktemp -d) - ffmpeg -i "$1" "$MYTEMPDIR/out%04d.gif" # Extracts each frame of the video as a single gif - convert -delay 4 "$MYTEMPDIR/out*.gif" "$MYTEMPDIR/anim.gif" # Combines all the frames into one very nicely animated gif. - convert -layers Optimize "$MYTEMPDIR/anim.gif" "$2" # Optimizes the gif using imagemagick - - # Cleans up the leftovers - rm -r "$MYTEMPDIR" - shift - ;; -esac - - +-h | --help) + myHelp + exit + ;; +*) + echo "Converting $1 to an gif file." + MYTEMPDIR=$(mktemp -d) + ffmpeg -i "$1" "${MYTEMPDIR}/out%04d.gif" # Extracts each frame of the video as a single gif + convert -delay 4 "${MYTEMPDIR}/out*.gif" "${MYTEMPDIR}/anim.gif" # Combines all the frames into one very nicely animated gif. + convert -layers Optimize "${MYTEMPDIR}/anim.gif" "$2" # Optimizes the gif using imagemagick + # Cleans up the leftovers + rm -r "${MYTEMPDIR}" + shift + ;; +esac diff --git a/bin/executable_pdf2eps.sh b/bin/executable_pdf2eps.sh index e11d560..7b62558 100644 --- a/bin/executable_pdf2eps.sh +++ b/bin/executable_pdf2eps.sh @@ -1,7 +1,7 @@ #!/bin/sh # -myHelp () { +myHelp() { cat <<-END Usage: ------ @@ -16,16 +16,16 @@ END while [ -n "$1" ]; do case "$1" in - -h | --help) - myHelp - exit - ;; - *) - echo "Converting $1.pdf to an eps file." - pdfcrop "$1.pdf" "$1-temp.pdf" - gs -q -dNOCACHE -dNOPAUSE -dBATCH -dSAFER -sDEVICE=eps2write -sOutputFile="$1.eps" "$1-temp.pdf" - rm "$1-temp.pdf" - shift - ;; - esac -done + -h | --help) + myHelp + exit + ;; + *) + echo "Converting $1.pdf to an eps file." + pdfcrop "$1.pdf" "$1-temp.pdf" + gs -q -dNOCACHE -dNOPAUSE -dBATCH -dSAFER -sDEVICE=eps2write -sOutputFile="$1.eps" "$1-temp.pdf" + rm "$1-temp.pdf" + shift + ;; + esac +done diff --git a/bin/executable_pdf2epsv2.sh b/bin/executable_pdf2epsv2.sh index 662d053..f42fc2a 100644 --- a/bin/executable_pdf2epsv2.sh +++ b/bin/executable_pdf2epsv2.sh @@ -1,7 +1,7 @@ #!/bin/sh # -myHelp () { +myHelp() { cat <<-END Usage: ------ @@ -14,16 +14,16 @@ END } case "$1" in - -h | --help) - myHelp - exit - ;; - *) - echo "Converting $1.pdf to an eps file called $2." - pdf2ps "$1" "$1-temp.ps"; - ps2eps -f "$1-temp.ps" ; - mv "$1-temp.eps" "$2"; - rm -f "$1-temp.ps"; - shift - ;; -esac +-h | --help) + myHelp + exit + ;; +*) + echo "Converting $1.pdf to an eps file called $2." + pdf2ps "$1" "$1-temp.ps" + ps2eps -f "$1-temp.ps" + mv "$1-temp.eps" "$2" + rm -f "$1-temp.ps" + shift + ;; +esac diff --git a/bin/executable_png2eps.sh b/bin/executable_png2eps.sh index 6255e00..db938eb 100644 --- a/bin/executable_png2eps.sh +++ b/bin/executable_png2eps.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash # converts png to eps # # 2003-04-09 Thomas Henlich @@ -25,58 +25,58 @@ TXTFILE=/tmp/png2eps.txt.$$ RM="rm -f" # get the resolution (if specified) from the PNG file -CHK=`pngcheck -v $1` +CHK=$(pngcheck -v "$1") # get height in pixels from the PNG file -HEIGHT=`echo $CHK | sed -ne 's/.* [0-9]\+ x \(.*\) image.*/\1/p;'` +HEIGHT=$(echo "${CHK}" | sed -ne 's/.* [0-9]\+ x \(.*\) image.*/\1/p;') -# a rowsperstrip parameter of 'height' (or more) is needed to generate one +# a rowsperstrip parameter of 'height' (or more) is needed to generate one # single strip of data in the TIFF file (reduces file size) -ROWSPERSTRIP="-rowsperstrip $HEIGHT" +ROWSPERSTRIP="-rowsperstrip ${HEIGHT}" # ROWSPERSTRIP="rowsperstrip 1000000" # for images with square pixels, pngcheck outputs a line like # chunk pHYs ... 9646x9646 pixels/meter (245 dpi) # and we can read the resolution in dpi directly -RES=`echo $CHK | sed -ne '/pHYs/s/.*(\(.*\) dpi.*/\1/p;'` +RES=$(echo "${CHK}" | sed -ne '/pHYs/s/.*(\(.*\) dpi.*/\1/p;') # for images with non-square pixels, pngcheck outputs a line like # chunk pHYs ... 9646x4803 pixels/meter -XRES=`echo $CHK | sed -ne '/pHYs/s/.*: \([0-9]*\)x.* pixels\/meter.*/\1/p;'` -YRES=`echo $CHK | sed -ne '/pHYs/s/.*x\([0-9]*\) pixels\/meter.*/\1/p;'` +XRES=$(echo "${CHK}" | sed -ne '/pHYs/s/.*: \([0-9]*\)x.* pixels\/meter.*/\1/p;') +YRES=$(echo "${CHK}" | sed -ne '/pHYs/s/.*x\([0-9]*\) pixels\/meter.*/\1/p;') # for images with no fixed resolution, pngcheck may output a line like # chunk pHYs ... 9646x4803 pixels/unit # we ignore it (like pdfTeX does) # set the command-line arguments for pnmtotiff which specify the resolution -if [[ "$RES" ]]; then - echo "png2eps: Image with square pixels ($RES dpi)" 1>&2 - RES='-xres '$RES' -yres '$RES +if [[ "${RES}" ]]; then + echo "png2eps: Image with square pixels (${RES} dpi)" 1>&2 + RES="-xres ${RES} -yres ${RES}" fi -if [[ -z "$RES" ]]; then - if [[ "$XRES" ]]; then - echo "png2eps: Image with non-square pixels" \ - "($XRES""x$YRES pixels/meter)" 1>&2 -# we still have to convert pixels/meter -> dpi - RES='-xres '`echo .0254*$XRES |bc`' -yres '`echo .0254*$YRES |bc`; - else -# no resolution was specified and so a default of 72 dpi will be used - echo "png2eps: No resolution specified, using default (72 dpi)" 1>&2; - fi +if [[ -z "${RES}" ]]; then + if [[ "${XRES}" ]]; then + echo "png2eps: Image with non-square pixels" \ + "(${XRES}x${YRES} pixels/meter)" 1>&2 + # we still have to convert pixels/meter -> dpi + RES="-xres $(echo ".0254*${XRES}" | bc) -yres $(echo ".0254*${YRES}" | bc)" + else + # no resolution was specified and so a default of 72 dpi will be used + echo "png2eps: No resolution specified, using default (72 dpi)" 1>&2 + fi fi # convert to tiff -pngtopnm -verbose -text $TXTFILE $1 | \ -pnmtotiff $ROWSPERSTRIP $RES $COMPRESS -indexbits=1,2,4,8 >$OUTFILE +pngtopnm -verbose -text "${TXTFILE}" "$1" | + pnmtotiff "${ROWSPERSTRIP}" "${RES}" "${COMPRESS}" -indexbits=1,2,4,8 >"${OUTFILE}" # get the title of the figure -TITLE="`sed -ne 's/^Title *\(.*\)/\1/p;' <$TXTFILE`" +TITLE="$(sed -ne 's/^Title *\(.*\)/\1/p;' <"${TXTFILE}")" # set to input file name if unspecified -[ -z "$TITLE" ] && TITLE=`basename $1` +[[ -z "${TITLE}" ]] && TITLE=$(basename "$1") # convert to EPS, replacing the %%Title comment -tiff2ps -2 -e $OUTFILE | sed -e "s/^\(%%Title: \).*/\1$TITLE/;" +tiff2ps -2 -e "${OUTFILE}" | sed -e "s/^\(%%Title: \).*/\1${TITLE}/;" # remove temp files -$RM $OUTFILE $TXTFILE \ No newline at end of file +${RM} "${OUTFILE}" "${TXTFILE}" diff --git a/bin/executable_pyenv_python_install.tmpl b/bin/executable_pyenv_python_install.tmpl index 6c355ef..581501b 100644 --- a/bin/executable_pyenv_python_install.tmpl +++ b/bin/executable_pyenv_python_install.tmpl @@ -45,15 +45,15 @@ OPTIMIZE=false while getopts ":o" opt; do case ${opt} in - o) - OPTIMIZE=true - ;; - \?) - usage - ;; + o) + OPTIMIZE=true + ;; + \?) + usage + ;; esac done -shift $((OPTIND -1)) +shift $((OPTIND - 1)) PYTHON_VERSION="${1}" if [[ -z "$PYTHON_VERSION" ]]; then @@ -61,7 +61,7 @@ if [[ -z "$PYTHON_VERSION" ]]; then fi # Skip if already installed -if [[ -n "$(pyenv versions --bare | grep "^${PYTHON_VERSION}$")" ]]; then +if pyenv versions --bare | grep -q "^${PYTHON_VERSION}$"; then exit 0 fi diff --git a/bin/executable_teatimer.sh b/bin/executable_teatimer.sh index f31def5..9880a51 100644 --- a/bin/executable_teatimer.sh +++ b/bin/executable_teatimer.sh @@ -4,16 +4,16 @@ # Input the time, defaults to 3 minutes if [[ -z "$1" ]]; then - TIME="3m"; + TIME="3m" else - TIME=$1; + TIME=$1 fi # Notification for Mac platform if [[ "$(uname)" == "Darwin" ]]; then # Wait the specified time (need to brew install coreutils) - gsleep "$TIME"; + gsleep "${TIME}" # Make sure sound is enabled osascript -e "set Volume 2" @@ -23,29 +23,28 @@ if [[ "$(uname)" == "Darwin" ]]; then # Also increase the notification banner time by doing: # defaults write com.apple.notificationcenterui bannerTime 15 # taken from: https://9to5mac.com/2014/01/30/how-to-change-os-x-banner-notification-duration-using-terminal/ - # terminal-notifier -title "Your tea is ready!" -message "" -sound default -contentImage "$HOME/bin/tea.jpg"; + # terminal-notifier -title "Your tea is ready!" -message "" -sound default -contentImage "${HOME}/bin/tea.jpg"; # Display notification osascript -e 'display notification "--" with title "Your tea is ready"' # Make my own sound aiff with the old system beep? and play it like this afplay /System/Library/Sounds/Funk.aiff - + # Make sure sound is disabled (but wait for previous command to end) - gsleep 1s; + gsleep 1s osascript -e "set Volume 0" - # Notification for GNU/Linux platform elif [[ "$(uname -s)" == "Linux" ]]; then # Wait the specified time - sleep "$TIME"; + sleep "${TIME}" # Send the notification - notify-send -t 10000 -i "$HOME/bin/tea.jpg" "Your tea is ready!"; + notify-send -t 10000 -i "${HOME}/bin/tea.jpg" "Your tea is ready!" - # Run the system beep thing. + # Run the system beep thing. # # In RHEL6, you first need to modprobe pcspkr you can add it to boot # by adding /etc/sysconfig/mymodules.modules and following a diff --git a/docker/entrypoint-test.sh b/docker/entrypoint-test.sh index 66cfce4..2e9f0a8 100644 --- a/docker/entrypoint-test.sh +++ b/docker/entrypoint-test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash export PATH="$HOME/.local/bin:$PATH" diff --git a/dot_local/share/zinit/plugins/hpc/hpc.plugin.zsh.tmpl b/dot_local/share/zinit/plugins/hpc/hpc.plugin.zsh.tmpl index 2f2269f..6170749 100644 --- a/dot_local/share/zinit/plugins/hpc/hpc.plugin.zsh.tmpl +++ b/dot_local/share/zinit/plugins/hpc/hpc.plugin.zsh.tmpl @@ -1,4 +1,4 @@ -# -*- mode: sh; sh-indentation: 4; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# -*- mode: sh; sh-shell: bash; sh-indentation: 4; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # Copyright (c) 2024 Marc Henry de Frahan @@ -10,9 +10,9 @@ # Then ${0:h} to get hpc's directory -if [[ ${zsh_loaded_plugins[-1]} != */personal && -z ${fpath[(r)${0:h}]} ]] { - fpath+=( "${0:h}" ) -} +if [[ ${zsh_loaded_plugins[-1]} != */personal && -z ${fpath[(r)${0:h}]} ]]; then + fpath+=("${0:h}") +fi # Standard hash for plugins, to not pollute the namespace typeset -gA Plugins @@ -24,50 +24,50 @@ if [[ -z "${SCRATCH}" ]] && [[ -d "{{ .scratch_root }}" ]]; then fi function _select-compute-node() { - if [[ ! -x "$(command -v squeue)" ]]; then - echo "squeue not found." >&2 - return 1 - fi + if [[ ! -x "$(command -v squeue)" ]]; then + echo "squeue not found." >&2 + return 1 + fi - if [[ ! -x "$(command -v fzf)" ]]; then - echo "fzf not found." >&2 - return 1 - fi + if [[ ! -x "$(command -v fzf)" ]]; then + echo "fzf not found." >&2 + return 1 + fi - local node=$(squeue -u ${USER} -o "%N %j %M" -h 2>/dev/null | fzf --select-1 --prompt="Select node: " | awk '{print $1}') + local node=$(squeue -u ${USER} -o "%N %j %M" -h 2>/dev/null | fzf --select-1 --prompt="Select node: " | awk '{print $1}') - if [ -z "${node}" ]; then - echo "No node selected, exiting." >&2 - return 1 - fi + if [[ -z "${node}" ]]; then + echo "No node selected, exiting." >&2 + return 1 + fi - echo "${node}" + echo "${node}" } function compute-ssh() { - local node - node=$(_select-compute-node) || return 1 + local node + node=$(_select-compute-node) || return 1 - echo "Connecting to ${node}..." - ssh -t -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} + echo "Connecting to ${node}..." + ssh -t -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} } function compute-docker() { - local node - node=$(_select-compute-node) || return 1 + local node + node=$(_select-compute-node) || return 1 - local container - container=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} \ - 'docker ps --format "{{ "{{.Names}}" }}"' 2>/dev/null | fzf --select-1 --prompt="Select container: ") + local container + container=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} \ + 'docker ps --format "{{ "{{.Names}}" }}"' 2>/dev/null | fzf --select-1 --prompt="Select container: ") - if [[ -z "$container" ]]; then - echo "No containers found or none selected." >&2 - return 1 - fi + if [[ -z "${container}" ]]; then + echo "No containers found or none selected." >&2 + return 1 + fi - echo "Connecting to ${node} and attaching to Docker container ${container}..." - ssh -t -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} \ - "docker exec -it ${container} zsh -l" + echo "Connecting to ${node} and attaching to Docker container ${container}..." + ssh -t -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${node} \ + "docker exec -it ${container} zsh -l" } diff --git a/dot_local/share/zinit/plugins/personal/personal.plugin.zsh.tmpl b/dot_local/share/zinit/plugins/personal/personal.plugin.zsh.tmpl index 2116f0f..91571d8 100644 --- a/dot_local/share/zinit/plugins/personal/personal.plugin.zsh.tmpl +++ b/dot_local/share/zinit/plugins/personal/personal.plugin.zsh.tmpl @@ -10,9 +10,9 @@ # Then ${0:h} to get plugin's directory -if [[ ${zsh_loaded_plugins[-1]} != */personal && -z ${fpath[(r)${0:h}]} ]] { - fpath+=( "${0:h}" ) -} +if [[ ${zsh_loaded_plugins[-1]} != */personal && -z ${fpath[(r)${0:h}]} ]]; then + fpath+=("${0:h}") +fi # Standard hash for plugins, to not pollute the namespace typeset -gA Plugins @@ -25,8 +25,8 @@ alias ..2="cd ../.." alias ..3="cd ../../.." alias ..4="cd ../../../.." alias ..5="cd ../../../../.." -alias ge="$EMACS_PLUGIN_LAUNCHER --no-wait" -alias e="$EMACS_PLUGIN_LAUNCHER -nw" +alias ge="${EMACS_PLUGIN_LAUNCHER} --no-wait" +alias e="${EMACS_PLUGIN_LAUNCHER} -nw" alias ek='emacsclient -e "(kill-emacs)"' alias rmeps="rm *.eps" alias rmpng="rm *.png" @@ -56,12 +56,12 @@ alias mmv="noglob zmv -W" # Cluster aliases if [[ -x "$(command -v qstat)" ]]; then - alias qs="qstat -u $USER"; - alias job_node_ids="qstat -u $USER -f | grep -e 'Job\ Id\|exec_host\|Job_Name'" + alias qs="qstat -u ${USER}" + alias job_node_ids="qstat -u ${USER} -f | grep -e 'Job\ Id\|exec_host\|Job_Name'" fi if [[ -x "$(command -v squeue)" ]]; then - alias qs="squeue -u $USER -o '%12i %.9P %20j %.2t %.10M %.6D %r %N'" + alias qs="squeue -u ${USER} -o '%12i %.9P %20j %.2t %.10M %.6D %r %N'" alias qnodes="sinfo -o '%24P %.5a %.12l %.16F'" fi @@ -71,23 +71,23 @@ fi function prettypath() { echo ${1-$PATH} | sed 's/:/\n/g'; } # Calls the teatimer script -function tt() { ~/bin/teatimer.sh "$@" &} +function tt() { ~/bin/teatimer.sh "$@" & } # Calls the pdf2eps script -function pdf2eps() { ~/bin/pdf2eps.sh "$@" &} +function pdf2eps() { ~/bin/pdf2eps.sh "$@" & } # refresh directory (if the directory was deleted from underneath us # and we are still in it, try to re-cd to the directory) -function recd() { cd `pwd`; } +function recd() { cd "$(pwd)"; } # Diff two files using emacs ediff # From: https://defunitive.wordpress.com/2011/07/23/invoking-emacs-ediff-from-the-command-line/ function ediff() { - if [ "X${2}" = "X" ]; then + if [[ -z "${2}" ]]; then echo "USAGE: ediff " else # The --eval flag takes lisp code and evaluates it with emacs - "$EMACS_PLUGIN_LAUNCHER" -nw --eval "(ediff-files \"$1\" \"$2\")" + "${EMACS_PLUGIN_LAUNCHER}" -nw --eval "(ediff-files \"$1\" \"$2\")" fi } @@ -116,15 +116,15 @@ function format-cpp() { # from: https://github.com/tensorflow/tensorboard/issues/179 multitb() ( set -eu - if [ $# -eq 0 ]; then + if [[ $# -eq 0 ]]; then printf >&2 'fatal: provide at least one logdir\n' return 1 fi tmpdir="$(mktemp -d)" for arg; do case "${arg}" in - /*) ln -s "${arg}" "${tmpdir}/" ;; - *) ln -s "${PWD}/${arg}" "${tmpdir}/" ;; + /*) ln -s "${arg}" "${tmpdir}/" ;; + *) ln -s "${PWD}/${arg}" "${tmpdir}/" ;; esac done exit_code=0 @@ -141,24 +141,24 @@ multitb() ( return "${exit_code}" ) -function conda-start(){ +function conda-start() { CONDA_EXEC="" CONDA_DIR="" if [[ -x "$(command -v conda)" ]]; then CONDA_DIR="$(conda info --base)" CONDA_EXEC="conda" - elif [ -d "${HOME}/miniconda3" ]; then + elif [[ -d "${HOME}/miniconda3" ]]; then CONDA_DIR="${HOME}/miniconda3" CONDA_EXEC="${CONDA_DIR}/conda" fi - if [ -d "${CONDA_DIR}" ]; then + if [[ -d "${CONDA_DIR}" ]]; then # >>> conda initialize >>> - __conda_setup="$('${CONDA_EXEC}' 'shell.zsh' 'hook' 2> /dev/null)" - if [ $? -eq 0 ]; then - eval "$__conda_setup" + __conda_setup="$('${CONDA_EXEC}' 'shell.zsh' 'hook' 2>/dev/null)" + if [[ $? -eq 0 ]]; then + eval "${__conda_setup}" else - if [ -f "${CONDA_DIR}/etc/profile.d/conda.sh" ]; then + if [[ -f "${CONDA_DIR}/etc/profile.d/conda.sh" ]]; then . "${CONDA_DIR}/etc/profile.d/conda.sh" else export PATH="${CONDA_DIR}/bin:$PATH" @@ -189,7 +189,7 @@ tab-reset() { # Change the color of the tab when using SSH # reset the color after the connection closes color-ssh() { - if [[ -n "$ITERM_SESSION_ID" ]]; then + if [[ -n "${ITERM_SESSION_ID}" ]]; then trap "tab-reset" INT EXIT if [[ "$*" =~ "dav1|dav2|dav3" ]]; then tab-color 24 90 169 diff --git a/dot_vscode/extensions/llvm-org.lldb-vscode-0.1.0/executable_install.sh b/dot_vscode/extensions/llvm-org.lldb-vscode-0.1.0/executable_install.sh index d6d513a..1fc2393 100644 --- a/dot_vscode/extensions/llvm-org.lldb-vscode-0.1.0/executable_install.sh +++ b/dot_vscode/extensions/llvm-org.lldb-vscode-0.1.0/executable_install.sh @@ -1,4 +1,4 @@ -#!/usr/local/bin/bash +#!/usr/bin/env bash # Install vscode extension for LLDB if [[ ! -x "$(command -v npm)" ]]; then echo "Please install npm" diff --git a/dot_zshrc.tmpl b/dot_zshrc.tmpl index 5fc7c29..cfbf656 100644 --- a/dot_zshrc.tmpl +++ b/dot_zshrc.tmpl @@ -2,7 +2,7 @@ # Initialization code that may require console input (password prompts, [y/n] # confirmations, etc.) must go above this block; everything else may go below. if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then - source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" + source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" fi export ZSH_COMPDUMP="${HOME}/.zcompdump" @@ -11,31 +11,31 @@ export ZSH_COMPDUMP="${HOME}/.zcompdump" [[ -z "$USER" ]] && export USER="$(whoami)" # Docker shares safe state from host home (cli history, zoxide, ssh) -if [[ -n "$IN_DOCKER" ]] && [[ -d "/host-home" ]]; then - if [[ -f "/host-home/.zsh_history" ]] && [[ ! -L "$HOME/.zsh_history" ]]; then - rm -f "$HOME/.zsh_history" 2>/dev/null - ln -sf /host-home/.zsh_history "$HOME/.zsh_history" +if [[ -n "${IN_DOCKER}" ]] && [[ -d "/host-home" ]]; then + if [[ -f "/host-home/.zsh_history" ]] && [[ ! -L "${HOME}/.zsh_history" ]]; then + rm -f "${HOME}/.zsh_history" 2>/dev/null + ln -sf /host-home/.zsh_history "${HOME}/.zsh_history" fi - if [[ -d "/host-home/.ssh" ]] && [[ ! -L "$HOME/.ssh" ]]; then - [[ -d "$HOME/.ssh" ]] && rm -rf "$HOME/.ssh" - ln -sf /host-home/.ssh "$HOME/.ssh" + if [[ -d "/host-home/.ssh" ]] && [[ ! -L "${HOME}/.ssh" ]]; then + [[ -d "${HOME}/.ssh" ]] && rm -rf "${HOME}/.ssh" + ln -sf /host-home/.ssh "${HOME}/.ssh" fi - if [[ -d "/host-home/.cursor" ]] && [[ ! -L "$HOME/.cursor" ]]; then - [[ -d "$HOME/.cursor" ]] && rm -rf "$HOME/.cursor" - ln -sf /host-home/.cursor "$HOME/.cursor" + if [[ -d "/host-home/.cursor" ]] && [[ ! -L "${HOME}/.cursor" ]]; then + [[ -d "${HOME}/.cursor" ]] && rm -rf "${HOME}/.cursor" + ln -sf /host-home/.cursor "${HOME}/.cursor" fi - if [[ -d "/host-home/.config/cursor" ]] && [[ ! -L "$HOME/.config/cursor" ]]; then - mkdir -p "$HOME/.config" - [[ -d "$HOME/.config/cursor" ]] && rm -rf "$HOME/.config/cursor" - ln -sf /host-home/.config/cursor "$HOME/.config/cursor" + if [[ -d "/host-home/.config/cursor" ]] && [[ ! -L "${HOME}/.config/cursor" ]]; then + mkdir -p "${HOME}/.config" + [[ -d "${HOME}/.config/cursor" ]] && rm -rf "${HOME}/.config/cursor" + ln -sf /host-home/.config/cursor "${HOME}/.config/cursor" fi - if [[ -d "/host-home/.claude" ]] && [[ ! -L "$HOME/.claude" ]]; then - [[ -d "$HOME/.claude" ]] && rm -rf "$HOME/.claude" - ln -sf /host-home/.claude "$HOME/.claude" + if [[ -d "/host-home/.claude" ]] && [[ ! -L "${HOME}/.claude" ]]; then + [[ -d "${HOME}/.claude" ]] && rm -rf "${HOME}/.claude" + ln -sf /host-home/.claude "${HOME}/.claude" fi fi @@ -49,14 +49,14 @@ ZINIT[BIN_DIR]="${ZINIT_HOME}/zinit.git" if [[ ! -f ${ZINIT[BIN_DIR]}/zinit.zsh ]]; then print -P "%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f" command mkdir -p "${ZINIT_HOME}" && command chmod g-rwX "${ZINIT_HOME}" - command git clone https://github.com/zdharma-continuum/zinit "${ZINIT[BIN_DIR]}" && \ - print -P "%F{33} %F{34}Installation successful.%f%b" || \ - print -P "%F{160} The clone has failed.%f%b" + command git clone https://github.com/zdharma-continuum/zinit "${ZINIT[BIN_DIR]}" && + print -P "%F{33} %F{34}Installation successful.%f%b" || + print -P "%F{160} The clone has failed.%f%b" fi source "${ZINIT[BIN_DIR]}/zinit.zsh" autoload -Uz _zinit -(( ${+_comps} )) && _comps[zinit]=_zinit +((${+_comps})) && _comps[zinit]=_zinit # Load a few important annexes, without Turbo # (this is currently required for annexes) @@ -74,10 +74,10 @@ zinit light-mode for \ export PATH="${HOME}/.local/bin:${HOME}/bin:${PATH}" # Unset XDG_RUNTIME_DIR if directory doesn't exist -[[ -n "$XDG_RUNTIME_DIR" && ! -d "$XDG_RUNTIME_DIR" ]] && unset XDG_RUNTIME_DIR +[[ -n "${XDG_RUNTIME_DIR}" && ! -d "${XDG_RUNTIME_DIR}" ]] && unset XDG_RUNTIME_DIR # homebrew -if [ -d "/opt/homebrew" ]; then +if [[ -d "/opt/homebrew" ]]; then eval "$(/opt/homebrew/bin/brew shellenv)" fi @@ -88,22 +88,22 @@ export MASA_HOME="${COMBUSTION_DIR}/install/MASA" # pyenv export PYENV_ROOT="{{ .large_install_dir }}/.local/pyenv" if [[ -d "${PYENV_ROOT}" ]]; then - command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" + command -v pyenv >/dev/null || export PATH="${PYENV_ROOT}/bin:$PATH" eval "$(pyenv init -)" fi # exawind-manager -if [ -d "${HOME}/exawind/exawind-manager" ]; then +if [[ -d "${HOME}/exawind/exawind-manager" ]]; then export EXAWIND_MANAGER=${HOME}/exawind/exawind-manager source ${EXAWIND_MANAGER}/start.sh fi # zoxide -export _ZO_DATA_DIR=$HOME/.local/share +export _ZO_DATA_DIR=${HOME}/.local/share # hunspell dicts -if [ -d "${HOME}/.local/share/hunspell/dicts" ]; then - export DICPATH="${HOME}/.local/share/hunspell/dicts" +if [[ -d "${HOME}/.local/share/hunspell/dicts" ]]; then + export DICPATH="${HOME}/.local/share/hunspell/dicts" fi # ollama @@ -121,8 +121,8 @@ export WORKON_HOME="{{ .large_install_dir }}/.local/virtualenvs" # paths for Mac OSX # github tokens -if [ -f "${HOME}/.github_tokens" ]; then - source "${HOME}/.github_tokens" +if [[ -f "${HOME}/.github_tokens" ]]; then + source "${HOME}/.github_tokens" fi # openmpi fails because tmp dir name is too long @@ -137,7 +137,7 @@ alias vlc="{HOME}/Applications/VLC.app/Contents/MacOS/VLC" # Enchant lib path for python if [[ ! -x "$(command -v pyenv)" ]]; then - export PYENCHANT_LIBRARY_PATH=/opt/homebrew/lib/libenchant-2.dylib + export PYENCHANT_LIBRARY_PATH=/opt/homebrew/lib/libenchant-2.dylib fi {{- end }} @@ -149,10 +149,10 @@ zinit ice lucid depth"1" zinit light romkatv/powerlevel10k zinit wait lucid light-mode \ - dl'https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/plugins/emacs/emacsclient.sh' \ - atclone'chmod +x emacsclient.sh' \ - atpull'%atclone' \ - for "OMZP::emacs" + dl'https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/plugins/emacs/emacsclient.sh' \ + atclone'chmod +x emacsclient.sh' \ + atpull'%atclone' \ + for "OMZP::emacs" # Install fzf and zoxide binaries (arch-specific via gh-r) zinit from"gh-r" as"program" for @junegunn/fzf @@ -168,17 +168,17 @@ zinit from"gh-r" as"program" atload' ' light-mode for @Schniz/fnm zinit wait lucid light-mode for \ - "OMZP::fzf" \ - has'fzf' @Aloxaf/fzf-tab \ - "OMZP::git" \ - "OMZP::poetry" \ - "OMZP::pyenv" \ - "OMZP::tmux" \ - "OMZP::zoxide" + "OMZP::fzf" \ + has'fzf' @Aloxaf/fzf-tab \ + "OMZP::git" \ + "OMZP::poetry" \ + "OMZP::pyenv" \ + "OMZP::tmux" \ + "OMZP::zoxide" zinit ice lucid from"gh-r" as"command" \ - mv"vivid-*/vivid -> vivid" \ - atload'export LS_COLORS="$(vivid generate one-dark)"; zstyle ":completion:*" list-colors "${(s.:.)LS_COLORS}"' + mv"vivid-*/vivid -> vivid" \ + atload'export LS_COLORS="$(vivid generate one-dark)"; zstyle ":completion:*" list-colors "${(s.:.)LS_COLORS}"' zinit light sharkdp/vivid zinit ice from="gh-r" as="program" mv="bat* -> bat" pick="bat/bat" atload="alias cat=bat" @@ -191,7 +191,7 @@ zinit load so-fancy/diff-so-fancy zinit wait lucid light-mode for "${HOME}/.local/share/zinit/plugins/personal" # Path to your oh-my-zsh installation. -export ZSH=$HOME/.oh-my-zsh +export ZSH=${HOME}/.oh-my-zsh # OMZL options zstyle ':omz:lib:theme-and-appearance' gnu-ls yes @@ -205,7 +205,7 @@ CORRECT_IGNORE_FILE='.*' # Add wisely, as too many plugins slow down shell startup. plugins=() -source $ZSH/oh-my-zsh.sh +source ${ZSH}/oh-my-zsh.sh # User configuration @@ -234,8 +234,8 @@ down-line-or-local-history() { } zle -N down-line-or-local-history -bindkey "^[[1;5A" up-line-or-history # [CTRL] + Cursor up -bindkey "^[[1;5B" down-line-or-history # [CTRL] + Cursor down +bindkey "^[[1;5A" up-line-or-history # [CTRL] + Cursor up +bindkey "^[[1;5B" down-line-or-history # [CTRL] + Cursor down # Use ctrl-p/n to go up/down search bindkey "^P" up-line-or-beginning-search @@ -244,17 +244,17 @@ bindkey "^P" up-line-or-beginning-search # History HISTFILE=~/.zsh_history HISTSIZE=1000000 -SAVEHIST=$HISTSIZE +SAVEHIST=${HISTSIZE} setopt HIST_IGNORE_ALL_DUPS setopt HIST_FIND_NO_DUPS # zsh history backups export HIST_BACKUP_DIR="${HOME}/.zsh_history_backups" -if [ ! -d "${HIST_BACKUP_DIR}" ]; then - mkdir "${HIST_BACKUP_DIR}" +if [[ ! -d "${HIST_BACKUP_DIR}" ]]; then + mkdir "${HIST_BACKUP_DIR}" fi -function backup_zsh_history () { +function backup_zsh_history() { if [[ -d "${HIST_BACKUP_DIR}" ]]; then local hist_pfx="zsh_history_" local hist_ext=".bak" @@ -264,7 +264,7 @@ function backup_zsh_history () { fi # Keep 20 most recent backups local -a old_backups=("${HIST_BACKUP_DIR}"/${hist_pfx}*${hist_ext}(NOm)) - if (( ${#old_backups[@]} > 20 )); then + if ((${#old_backups[@]} > 20)); then rm -f "${old_backups[@]:20}" 2>/dev/null fi fi @@ -272,8 +272,8 @@ function backup_zsh_history () { backup_zsh_history -function backup_zoxide_database () { - if [[ -d "$_ZO_DATA_DIR" ]]; then +function backup_zoxide_database() { + if [[ -d "${_ZO_DATA_DIR}" ]]; then local zo_pfx="db_" local zo_ext="-bak.zo" local zo_data="${_ZO_DATA_DIR}/db.zo" @@ -283,7 +283,7 @@ function backup_zoxide_database () { fi # Keep 7 most recent backups local -a old_backups=("${_ZO_DATA_DIR}"/${zo_pfx}*${zo_ext}(NOm)) - if (( ${#old_backups[@]} > 7 )); then + if ((${#old_backups[@]} > 7)); then rm -f "${old_backups[@]:7}" 2>/dev/null fi fi @@ -292,7 +292,7 @@ function backup_zoxide_database () { backup_zoxide_database # Source shell secrets (prefer host secrets when in Docker) -if [[ -n "$IN_DOCKER" ]] && [[ -f "/host-home/.shell-secrets" ]]; then +if [[ -n "${IN_DOCKER}" ]] && [[ -f "/host-home/.shell-secrets" ]]; then source "/host-home/.shell-secrets" elif [[ -f "${HOME}/.shell-secrets" ]]; then source "${HOME}/.shell-secrets" diff --git a/run_after_install-hunspell.sh b/run_after_install-hunspell.sh index ee95bc4..1976494 100644 --- a/run_after_install-hunspell.sh +++ b/run_after_install-hunspell.sh @@ -12,8 +12,8 @@ if [[ -x "$(command -v hunspell)" ]]; then HUNSPELL_EN_US_NAME="hunspell-en_US-2020.12.07.zip" mkdir -p "${HUNSPELL_DICT_LOCATION}" - cd "${HUNSPELL_DICT_LOCATION}" + cd "${HUNSPELL_DICT_LOCATION}" || exit 1 wget "${HUNSPELL_EN_US_BASE_URL}${HUNSPELL_EN_US_NAME}" - unzip "${HUNSPELL_EN_US_NAME}" *.aff *.dic -d "${HUNSPELL_DICT_LOCATION}" + unzip "${HUNSPELL_EN_US_NAME}" -- "*.aff" "*.dic" -d "${HUNSPELL_DICT_LOCATION}" rm "${HUNSPELL_EN_US_NAME}" fi diff --git a/run_after_install-packages-linux-post.sh.tmpl b/run_after_install-packages-linux-post.sh.tmpl index e9ef19c..8f4f6d1 100644 --- a/run_after_install-packages-linux-post.sh.tmpl +++ b/run_after_install-packages-linux-post.sh.tmpl @@ -1,7 +1,7 @@ {{- if eq .chezmoi.os "linux" -}} #!/usr/bin/env bash -bash ${HOME}/bin/install_fonts.sh -bash ${HOME}/bin/install_emms-print-metadata.sh +bash "${HOME}/bin/install_fonts.sh" +bash "${HOME}/bin/install_emms-print-metadata.sh" {{ end -}} \ No newline at end of file diff --git a/run_after_install-python-setup.sh.tmpl b/run_after_install-python-setup.sh.tmpl index e8d845e..4df1663 100644 --- a/run_after_install-python-setup.sh.tmpl +++ b/run_after_install-python-setup.sh.tmpl @@ -14,12 +14,12 @@ fi # Initialize pyenv if [[ -d "${PYENV_ROOT}" ]]; then - command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" + command -v pyenv >/dev/null || export PATH="${PYENV_ROOT}/bin:${PATH}" eval "$(pyenv init -)" fi # Install Python version and set as global -${HOME}/bin/pyenv_python_install ${PYTHON_VERSION} +"${HOME}/bin/pyenv_python_install" "${PYTHON_VERSION}" # Install uv if not present if [[ ! -x "$(command -v uv)" ]]; then diff --git a/run_before_install-packages-linux.sh.tmpl b/run_before_install-packages-linux.sh.tmpl index 35a2b9c..335eace 100644 --- a/run_before_install-packages-linux.sh.tmpl +++ b/run_before_install-packages-linux.sh.tmpl @@ -3,7 +3,7 @@ # uv if [[ ! -x "$(command -v uv)" ]]; then - curl -LsSf https://astral.sh/uv/install.sh | sh + curl -LsSf https://astral.sh/uv/install.sh | sh fi {{ end -}} From 35339296da9a23916bdaf2fe31e1b78dbbcf881f Mon Sep 17 00:00:00 2001 From: Marc Henry de Frahan Date: Fri, 10 Apr 2026 12:04:27 -0600 Subject: [PATCH 2/4] add ci check --- .chezmoiignore | 1 + .github/workflows/ci.yml | 11 ++++++++++- ...hezmoi_shell_linter.sh => chezmoi_shell_linter.sh} | 0 3 files changed, 11 insertions(+), 1 deletion(-) rename bin/{executable_chezmoi_shell_linter.sh => chezmoi_shell_linter.sh} (100%) diff --git a/.chezmoiignore b/.chezmoiignore index fa1ff75..c629c46 100644 --- a/.chezmoiignore +++ b/.chezmoiignore @@ -6,6 +6,7 @@ requirements.txt .shell-secrets docker .dockerignore +bin/chezmoi_shell_linter.sh {{- if ne .chezmoi.os "darwin" }} .config/karabiner diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1c9240..d9ea3d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,11 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - os: [ubuntu-latest, macos-latest] + include: + - os: ubuntu-latest + install_linters: sudo apt-get update && sudo apt-get install -y shellcheck + - os: macos-latest + install_linters: brew install shellcheck steps: - name: Checkout uses: actions/checkout@v4 @@ -26,6 +30,8 @@ jobs: - name: Dependencies run: | bash ./dotfiles/bin/executable_install_bootstrap_dependencies.sh + curl -sS https://webi.sh/shfmt | sh + ${{matrix.install_linters}} - name: Add local bin to PATH run: | echo "$HOME/.local/bin" >> $GITHUB_PATH @@ -34,6 +40,9 @@ jobs: mkdir -p "$HOME/.local/share/chezmoi" git config --global --add safe.directory "$HOME/.local/share/chezmoi" git clone --depth 1 --branch "${{ github.head_ref || github.ref_name }}" https://github.com/marchdf/dotfiles.git "$HOME/.local/share/chezmoi" + - name: Lint shell scripts + run: | + bash "$HOME/.local/share/chezmoi/bin/chezmoi_shell_linter.sh" - name: Install run: | chezmoi init --source="$HOME/.local/share/chezmoi" --promptBool test_machine=t,"Use ZSH_ROOT_DIR for tmux shell"=f --promptString email="" diff --git a/bin/executable_chezmoi_shell_linter.sh b/bin/chezmoi_shell_linter.sh similarity index 100% rename from bin/executable_chezmoi_shell_linter.sh rename to bin/chezmoi_shell_linter.sh From 522935ca35c4fee04379ffdd317ab53aa879e977 Mon Sep 17 00:00:00 2001 From: Marc Henry de Frahan Date: Fri, 10 Apr 2026 12:12:07 -0600 Subject: [PATCH 3/4] osx bash --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9ea3d2..b6712c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - os: ubuntu-latest install_linters: sudo apt-get update && sudo apt-get install -y shellcheck - os: macos-latest - install_linters: brew install shellcheck + install_linters: brew install shellcheck bash steps: - name: Checkout uses: actions/checkout@v4 From 96cdec73495bb5960664e833a354b1f467ab07ba Mon Sep 17 00:00:00 2001 From: Marc Henry de Frahan Date: Fri, 10 Apr 2026 12:29:55 -0600 Subject: [PATCH 4/4] fix name issue --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6712c2..7e4600e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,7 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - include: - - os: ubuntu-latest - install_linters: sudo apt-get update && sudo apt-get install -y shellcheck - - os: macos-latest - install_linters: brew install shellcheck bash + os: [ubuntu-latest, macos-latest] steps: - name: Checkout uses: actions/checkout@v4 @@ -31,7 +27,12 @@ jobs: run: | bash ./dotfiles/bin/executable_install_bootstrap_dependencies.sh curl -sS https://webi.sh/shfmt | sh - ${{matrix.install_linters}} + if [[ "$RUNNER_OS" == "Linux" ]]; then + sudo apt-get update && sudo apt-get install -y shellcheck + else + brew install shellcheck bash + fi + shell: bash - name: Add local bin to PATH run: | echo "$HOME/.local/bin" >> $GITHUB_PATH