From af47f8b03bbc10d8553dbdead7eeeccfe0cb47b5 Mon Sep 17 00:00:00 2001 From: Jor Sanders Date: Sat, 25 Jul 2026 03:16:08 +0200 Subject: [PATCH 1/5] feat: posix rewrite, git workflow scripts and install script - Rewrite all scripts as POSIX sh, shellcheck clean - gc: smarter checkout (tags, multi-remote, worktrees, master/main alias) - cb: clean local branches with no remote counterpart - sq: squash branch with force-with-lease push and stacked-branch guard - ec: empty commit and push - sw: regex project search under $PROJECTS_DIR - Fix broken checksum libs, make them standalone executables - Add install script and bin/local/ for uncommitted machine-local scripts - Remove crb, wsldockerd, config/ and lib/project_root - Update CI to checkout@v7 and super-linter/slim@v8 Co-Authored-By: Claude Fable 5 --- .github/workflows/CI-CD.yml | 6 +- .gitignore | 4 + README.md | 26 +--- aliasses/git | 3 +- aliasses/kubectl | 2 +- aliasses/minikube | 2 +- aliasses/utils | 2 +- aliasses/vim | 2 +- bin/cb | 52 +++++++- bin/crb | 33 ----- bin/create_base64 | 4 +- bin/create_certificate_fingerprint | 4 +- bin/create_certkey | 4 +- bin/create_csr | 4 +- bin/create_csr_cert | 4 +- bin/create_keystore | 4 +- bin/create_keystore_jks | 4 +- bin/create_truststore | 4 +- bin/create_truststore_jks | 4 +- bin/ec | 32 +++++ bin/gacp | 4 +- bin/gc | 199 +++++++++++++++++++++++------ bin/lib/checksum_changed | 23 +--- bin/lib/cof | 15 +-- bin/lib/project_root | 29 ----- bin/lib/save_checksum | 16 +-- bin/local/README.md | 5 + bin/mci | 21 +-- bin/sb | 40 +++--- bin/sq | 108 ++++++++++++++++ bin/sw | 74 +++++++++-- bin/wip | 4 +- bin/wsldockerd | 17 --- claude.md | 1 + config/ssh/config | 6 - install | 48 +++++++ profile/keybinds-osx | 10 ++ profile/keychain-osx | 8 +- profile/keychain-rpi | 14 +- profile/keychain-wsl | 16 ++- profile/ksops | 4 +- profile/kubectl | 4 +- profile/less | 2 +- profile/nvm | 32 ++--- profile/projects | 3 + profile/pyenv | 4 +- profile/sentry | 2 +- profile/vim | 2 +- profile/wsl | 4 +- 49 files changed, 610 insertions(+), 305 deletions(-) delete mode 100755 bin/crb create mode 100755 bin/ec delete mode 100755 bin/lib/project_root create mode 100644 bin/local/README.md create mode 100755 bin/sq delete mode 100755 bin/wsldockerd create mode 100644 claude.md delete mode 100644 config/ssh/config create mode 100755 install create mode 100755 profile/keybinds-osx create mode 100755 profile/projects diff --git a/.github/workflows/CI-CD.yml b/.github/workflows/CI-CD.yml index 300ee16..51b2b94 100644 --- a/.github/workflows/CI-CD.yml +++ b/.github/workflows/CI-CD.yml @@ -9,8 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v7 + with: + fetch-depth: 0 - name: Run Super-Linter - uses: github/super-linter@latest + uses: super-linter/super-linter/slim@v8 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 561b90f..29bef76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ ; Secret files are for tokens or other "Secret stuff" *.secret .DS_Store + +; Machine-local scripts (work-specific etc.), only the README is committed +bin/local/* +!bin/local/README.md diff --git a/README.md b/README.md index 077b1b1..46cfec6 100644 --- a/README.md +++ b/README.md @@ -7,26 +7,10 @@ I highly recommend not running other people commands blindly on your machine. So ## Setup -### .bashrc -Add the following to your .bashrc +Run the install script. It adds bin/ and bin/local/ (gitignored, +machine-local scripts) to PATH and sources every file under profile/ and +aliasses/ from your shell rc file (~/.zshrc for zsh, ~/.bashrc for bash), +see `./install --help`: ```shell -# Jor bin in PATH -export PATH="$HOME/Projects/Scripts/bin:$PATH" - -# Jor .profile -for f in $HOME/Projects/Scripts/profile/*; do - . "$f" -done - -# Jor aliasses -for f in $HOME/Projects/Scripts/aliasses/*; do - . "$f" -done -``` - -### Config -```shell -mkdir -p ~/.ssh -cp config/ssh/config ~/.ssh/config -chmod 600 ~/.ssh/config +./install ``` diff --git a/aliasses/git b/aliasses/git index e56fe48..aafbf35 100755 --- a/aliasses/git +++ b/aliasses/git @@ -1,4 +1,3 @@ -#!/bin/bash +#!/bin/sh alias grh="git reset --hard" -# alias git-squash="git reset $(git merge-base master $(git "$1" --show-current))" diff --git a/aliasses/kubectl b/aliasses/kubectl index 438e002..1278dea 100755 --- a/aliasses/kubectl +++ b/aliasses/kubectl @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh alias k8='kubectl' diff --git a/aliasses/minikube b/aliasses/minikube index 1ad06fa..43a29b4 100755 --- a/aliasses/minikube +++ b/aliasses/minikube @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh alias podmani='podman machine init --cpus 4 --memory 4096 --disk-size 100' alias minikubed='minikube start --driver=podman --container-runtime containerd' #--alsologtostderr diff --git a/aliasses/utils b/aliasses/utils index 6e8439c..4728910 100755 --- a/aliasses/utils +++ b/aliasses/utils @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh alias conn='ping 8.8.8.8|grep -ivE "time=[0-9][0-9]\."' diff --git a/aliasses/vim b/aliasses/vim index 3cb9c9a..7b7e709 100755 --- a/aliasses/vim +++ b/aliasses/vim @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh alias vi='vim' diff --git a/bin/cb b/bin/cb index f130e5b..a5bde2b 100755 --- a/bin/cb +++ b/bin/cb @@ -1,8 +1,50 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then - echo "Clean all local git branches except master|development|develop" - exit 0 +usage() { + cat <<'EOF' +Usage: cb + +Cleans up local git branches: deletes every local branch that has no +branch of the same name on any remote. + +Runs 'git fetch --prune' first so the remote state is current. The +currently checked out branch is never deleted, and branches checked out +in other worktrees are skipped. +EOF +} + +case "$1" in + -h|--help) + usage + exit 0 + ;; +esac + +if [ -z "$(git remote)" ]; then + echo "No remotes configured, nothing to compare against" >&2 + exit 1 fi -git branch | grep -iEv "master|development|develop|\\*" | xargs git branch -D +git fetch --prune + +current=$(git rev-parse --abbrev-ref HEAD) + +# Branches checked out in a worktree, one refs/heads/... per line. +in_worktrees=$(git worktree list --porcelain | awk '/^branch /{print substr($0, 8)}') + +git for-each-ref --format='%(refname:short)' refs/heads | while read -r branch; do + [ "$branch" = "$current" ] && continue + + if printf '%s\n' "$in_worktrees" | grep -Fxq "refs/heads/$branch"; then + echo "Skipping '$branch', it is checked out in a worktree" + continue + fi + + for remote in $(git remote); do + if git rev-parse --verify --quiet "refs/remotes/$remote/$branch" >/dev/null; then + continue 2 + fi + done + + git branch -D "$branch" +done diff --git a/bin/crb b/bin/crb deleted file mode 100755 index 35e8920..0000000 --- a/bin/crb +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# Clean remote branches - -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then - echo "Clean all remote git branches except master|development|develop" - echo "\$1 blacklist regex, matches won't be removed" - echo "\$2 NODRYRUN to actually run this" - exit 0 -fi - -WHITELIST="origin/master$|origin/development$|origin/develop$|origin/HEAD" -if [ "$1" != "" ]; then - WHITELIST="$WHITELIST|$1" -fi - -echo "Removing branches:" -DYINGBRANCHES=$(git branch -r | grep -iEv "$WHITELIST") -for DYINGBRANCH in $DYINGBRANCHES -do - BRANCHNAME=${DYINGBRANCH#"origin/"} - if [ "$2" == "NODRYRUN" ]; then - echo "No dry run, removing $BRANCHNAME now!!!!!!!!!!" - git push origin --delete "$BRANCHNAME" - else - echo "Dry run, pretend removing $BRANCHNAME" - fi -done - -echo "Branches kept:" -git branch -r | grep -iE "$WHITELIST" - -echo "$WHITELIST" diff --git a/bin/create_base64 b/bin/create_base64 index 7e5f739..cd4e36d 100755 --- a/bin/create_base64 +++ b/bin/create_base64 @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "_base64.txt copy of file" echo "\$1 original filename" exit 0 diff --git a/bin/create_certificate_fingerprint b/bin/create_certificate_fingerprint index 73b701e..ada99b3 100755 --- a/bin/create_certificate_fingerprint +++ b/bin/create_certificate_fingerprint @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Get SHA1 fingerprint of a .pem file." echo "\$1 .pem cert" exit 0 diff --git a/bin/create_certkey b/bin/create_certkey index a3edf81..5ed4cf7 100755 --- a/bin/create_certkey +++ b/bin/create_certkey @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Create an RSA key.pem and cert.pem" echo "\$1 CN for the cert" exit 0 diff --git a/bin/create_csr b/bin/create_csr index dcd34e9..58099c5 100755 --- a/bin/create_csr +++ b/bin/create_csr @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 2 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 2 ] || [ "$1" = "--help" ]; then echo "Create a csr" echo "\$1 key file .pem" echo "\$2 CN for the csr" diff --git a/bin/create_csr_cert b/bin/create_csr_cert index 94a234b..454c81a 100755 --- a/bin/create_csr_cert +++ b/bin/create_csr_cert @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 3 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 3 ] || [ "$1" = "--help" ]; then echo "Create an validate a CSR en generate valid_cert.pem" echo "\$1 .csr file" echo "\$2 CA cert.pem" diff --git a/bin/create_keystore b/bin/create_keystore index 1fc4b2a..7e50de2 100755 --- a/bin/create_keystore +++ b/bin/create_keystore @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Create a PKCS12 keystore of a .pem" echo "\$1 .pem file" exit 0 diff --git a/bin/create_keystore_jks b/bin/create_keystore_jks index 1537ca5..efdd40a 100755 --- a/bin/create_keystore_jks +++ b/bin/create_keystore_jks @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 2 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 2 ] || [ "$1" = "--help" ]; then echo "Create a JKS keystore of a PKCS12 keystore" echo "\$1 .pkcs12 keystore file" echo "\$2 .jks keystore file" diff --git a/bin/create_truststore b/bin/create_truststore index aa76f31..180671d 100755 --- a/bin/create_truststore +++ b/bin/create_truststore @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Create a PKCS12 (.pfx) truststore of a .pem file" echo "\$1 .pem file" exit 0 diff --git a/bin/create_truststore_jks b/bin/create_truststore_jks index 54f3516..283b331 100755 --- a/bin/create_truststore_jks +++ b/bin/create_truststore_jks @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [ $# -lt 3 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 3 ] || [ "$1" = "--help" ]; then echo "Create a .jks truststore of a .pem file. Or add a pem to a truststore" echo "\$1 .pem cert" echo "\$2 alias for the cert in the truststore" diff --git a/bin/ec b/bin/ec new file mode 100755 index 0000000..3f86df9 --- /dev/null +++ b/bin/ec @@ -0,0 +1,32 @@ +#!/bin/sh + +usage() { + cat <<'EOF' +Usage: ec + +Creates an empty commit with the message 'chore: empty commit' and pushes +it, e.g. to trigger CI. + +When the branch has no upstream yet, it is pushed with -u to set one. +EOF +} + +case "$1" in + -h|--help) + usage + exit 0 + ;; +esac + +git commit --allow-empty -m "chore: empty commit" || exit 1 + +if git rev-parse --verify --quiet '@{u}' >/dev/null 2>&1; then + git push +else + remote=$(git remote | head -n 1) + if [ -z "$remote" ]; then + echo "No remote to push to" >&2 + exit 1 + fi + git push -u "$remote" HEAD +fi diff --git a/bin/gacp b/bin/gacp index ab1135f..372d2a0 100755 --- a/bin/gacp +++ b/bin/gacp @@ -1,8 +1,8 @@ -#!/bin/bash +#!/bin/sh set -e -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "git adds, commits and pushes" echo "\$1 is the commit message" exit 0 diff --git a/bin/gc b/bin/gc index dadf473..a7a555f 100755 --- a/bin/gc +++ b/bin/gc @@ -1,54 +1,173 @@ -#!/bin/bash - -if [ $# -lt 1 ] || [[ $* == *"--help"* ]]; then - echo "Does a git checkout on a rev. Searching in this order" - echo " - Git tag" - echo " - Local branch" - echo " - Remote branch" - echo " - Creates a new branch branch" - exit 0 -fi +#!/bin/sh + +usage() { + cat <<'EOF' +Usage: gc + +Finds a git rev matching and checks it out. may also be a +partial (case-insensitive) match of a branch or tag name. + +Well-known branch aliases are swapped when the requested branch does not +exist but its counterpart does: master <-> main, develop <-> development. +So 'gc master' checks out 'main' in a repo that only has 'main'. + +Search order: + 1. Exact tag + 2. Exact local branch (followed by a git pull) + 3. Remote-prefixed branch, e.g. 'origin/my-branch' checks out 'my-branch' + 4. Exact branch on any remote (checked out as a tracking branch) + 5. Any other rev, e.g. a commit hash (detached HEAD) + 6. Partial match on local branch names + 7. Partial match on remote branch names (matched against 'remote/branch') + 8. Partial match on tag names + 9. Nothing matched: a new branch named is created + +A local branch that is checked out in another worktree (locked or not) has +that worktree removed before being checked out here. A worktree holding +uncommitted changes is left alone and gc exits with an error instead. + +If a match is ambiguous (multiple names, or the same branch on multiple +remotes), all matches are listed and nothing is checked out. Disambiguate +remotes with a prefix, e.g. 'gc upstream/my-branch'. +EOF +} + +case "$1" in + ''|-h|--help) + usage + exit 0 + ;; +esac + +rev=$1 + +# Checks out a local branch and pulls it when it has an upstream. When +# another worktree (locked or not) already has the branch checked out, that +# worktree is removed first, unless it holds uncommitted changes. +checkout_branch() { + other=$(git worktree list --porcelain | awk -v ref="refs/heads/$1" ' + /^worktree / { path = substr($0, 10) } + /^branch / { if (substr($0, 8) == ref) print path }') + if [ -n "$other" ] && [ "$other" != "$(git rev-parse --show-toplevel)" ]; then + echo "Branch '$1' is checked out in worktree $other, removing it" + git worktree unlock "$other" >/dev/null 2>&1 + if ! git worktree remove "$other"; then + echo "Could not remove worktree $other (uncommitted changes?)." >&2 + echo "Clean it up or run: git worktree remove --force '$other'" >&2 + return 1 + fi + fi + git checkout "$1" || return 1 + if git rev-parse --verify --quiet '@{u}' >/dev/null 2>&1; then + git pull + fi +} + +# Checks out a single partial match, or lists them all when ambiguous. +checkout_match() { + kind=$1 + matches=$2 + + [ -n "$matches" ] || return 1 + + count=$(printf '%s\n' "$matches" | wc -l | tr -d ' ') + if [ "$count" -gt 1 ]; then + echo "'$rev' matches multiple $kind:" >&2 + printf '%s\n' "$matches" | sed 's/^/ /' >&2 + exit 1 + fi + + case "$kind" in + 'local branches') + echo "Checking out local branch '$matches' (matched '$rev')" + checkout_branch "$matches" + ;; + 'remote branches') + branch=${matches#*/} + echo "Checking out remote branch '$matches' as '$branch'" + git checkout -b "$branch" --track "$matches" + ;; + 'tags') + echo "Checking out tag '$matches' (matched '$rev')" + git checkout "tags/$matches" + ;; + esac + exit $? +} + +# Returns the well-known counterpart of a branch name, if any. +alias_for() { + case "$1" in + master) echo main ;; + main) echo master ;; + develop) echo development ;; + development) echo develop ;; + esac +} + +# True when the branch exists locally or on any remote. +branch_exists() { + git rev-parse --verify --quiet "refs/heads/$1" >/dev/null && return 0 + for remote in $(git remote); do + git rev-parse --verify --quiet "refs/remotes/$remote/$1" >/dev/null && + return 0 + done + return 1 +} git fetch --prune -if git rev-parse --verify tags/"$1" > /dev/null 2>&1; then - echo "Checking out local tags/$1" - git checkout tags/"$1" - exit 0 +alias=$(alias_for "$rev") +if [ -n "$alias" ] && ! branch_exists "$rev" && branch_exists "$alias"; then + echo "No branch '$rev' here, using '$alias' instead" + rev=$alias fi -if git rev-parse --verify "$1" > /dev/null 2>&1; then - echo "Checking out local branch $1" - git checkout "$1" - git pull - exit 0 +if git rev-parse --verify --quiet "refs/tags/$rev" >/dev/null; then + echo "Checking out tag '$rev'" + git checkout "tags/$rev" + exit $? fi -if git rev-parse --verify origin/"$1" > /dev/null 2>&1; then - echo "Checking out remote branch $1" - git checkout origin/"$1" -b "$1" - exit 0 +if git rev-parse --verify --quiet "refs/heads/$rev" >/dev/null; then + echo "Checking out local branch '$rev'" + checkout_branch "$rev" + exit $? fi -if git rev-parse --verify origin/"$1" > /dev/null 2>&1; then - echo "Checking out remote branch $1" - git checkout origin/"$1" -b "$1" - exit 0 +if git rev-parse --verify --quiet "refs/remotes/$rev" >/dev/null; then + branch=${rev#*/} + if git rev-parse --verify --quiet "refs/heads/$branch" >/dev/null; then + echo "Checking out local branch '$branch' for '$rev'" + checkout_branch "$branch" + else + echo "Checking out remote branch '$rev'" + git checkout -b "$branch" --track "$rev" + fi + exit $? fi -LOCAL_BRANCH=$(git branch|grep -E "$1" -m 1 | xargs) -if [ -n "$LOCAL_BRANCH" ]; then - echo "!!! REGEX MATCHED LOCAL BRANCH '$LOCAL_BRANCH'" - git checkout "$LOCAL_BRANCH" - exit 0 -fi +checkout_match 'remote branches' \ + "$(for remote in $(git remote); do + git rev-parse --verify --quiet "refs/remotes/$remote/$rev" >/dev/null && + echo "$remote/$rev" + done)" -REMOTE_BRANCH=$(git branch -r|grep -E "$1" -m 1 | xargs | sed -e "s/^origin\///") -if [ -n "$REMOTE_BRANCH" ]; then - echo "!!! REGEX MATCHED REMOTE BRANCH '$REMOTE_BRANCH'" - git checkout origin/"$REMOTE_BRANCH" -b "$REMOTE_BRANCH" - exit 0 +if git rev-parse --verify --quiet "$rev^{commit}" >/dev/null; then + echo "Checking out rev '$rev' (detached HEAD)" + git checkout "$rev" + exit $? fi -echo "!!! CHECKING OUT NEW BRANCH $1" -git checkout -b "$1" +checkout_match 'local branches' \ + "$(git for-each-ref --format='%(refname:short)' refs/heads | grep -i -- "$rev")" + +checkout_match 'remote branches' \ + "$(git for-each-ref --format='%(refname)' refs/remotes | + sed 's|^refs/remotes/||' | grep -v '/HEAD$' | grep -i -- "$rev")" + +checkout_match 'tags' \ + "$(git for-each-ref --format='%(refname:short)' refs/tags | grep -i -- "$rev")" + +echo "No tag, branch, or rev matching '$rev' found, creating branch '$rev'" +git checkout -b "$rev" diff --git a/bin/lib/checksum_changed b/bin/lib/checksum_changed index 7cb618d..9198fc9 100755 --- a/bin/lib/checksum_changed +++ b/bin/lib/checksum_changed @@ -1,22 +1,13 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then - echo "Check if the checksum of a file changed since last check" +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then + echo "Check if the checksum of a file changed since it was last saved" + echo "with save_checksum. Exits 0 when it changed, 1 when it did not." echo "\$1 is the file name" exit 0 fi -checksum_changed () { - # shellcheck source=/dev/null - source "${BASH_SOURCE%/*}/cof" - CACHED_SHASUM=$(cof "$1") +current=$("$(dirname "$0")/cof" "$1") +cached=$(cat ".globalignored/$1.checksum" 2>/dev/null) - mkdir -p .globalignored/ - touch .globalignored/"$1".checksum - - if [[ $CACHED_SHASUM = .globalignored/"$1".checksum ]] ; then - exit 1 - fi - - exit 0 -} +[ "$current" != "$cached" ] diff --git a/bin/lib/cof b/bin/lib/cof index 15983df..a4fdff6 100755 --- a/bin/lib/cof +++ b/bin/lib/cof @@ -1,16 +1,9 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Print the sha256 checksum of a file" + echo "\$1 is the file name" exit 0 fi -cof () { - # Sha 256 checksum of file - SHASUM_OUTPUT=$(shasum -a 256 "$1") - - # Print the first element of the shasum output - # shellcheck disable=SC2086 - set -- $SHASUM_OUTPUT - echo "$1" -} +shasum -a 256 "$1" | cut -d' ' -f1 diff --git a/bin/lib/project_root b/bin/lib/project_root deleted file mode 100755 index eff7db9..0000000 --- a/bin/lib/project_root +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -if [[ $* == *"--help"* ]]; then - echo "Finds the full path of a project onder ~/Projects" - exit 0 -fi - -project_root () { - # Find the root of a project if it is located on Source. Also check for some common prefixes - # TODO make DRY - - DIR=$(find -L ~/Projects -maxdepth 2 -type d -iname "$1") - - if [ "$DIR" != "" ]; then - echo "$DIR" - exit 0 - fi - - - DIR=$(find -L ~/Projects -maxdepth 2 -type d -iname "nos-$1") - - if [ "$DIR" != "" ]; then - echo "$DIR" - exit 0 - fi - - echo "'$1' not found" 1>&2 - exit 1 -} diff --git a/bin/lib/save_checksum b/bin/lib/save_checksum index acdf75a..d0a58c4 100755 --- a/bin/lib/save_checksum +++ b/bin/lib/save_checksum @@ -1,15 +1,11 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then +if [ $# -lt 1 ] || [ "$1" = "--help" ]; then echo "Save the checksum of a file under .globalignored." - echo "Useful for detecting gemfile.locks and pom.xml changes." + echo "Useful for detecting Gemfile.lock and pom.xml changes." + echo "\$1 is the file name" exit 0 fi -save_checksum () { - mkdir -p .globalignored/ - touch .globalignored/"$1".checksum - # shellcheck source=/dev/null - source "${BASH_SOURCE%/*}/save_checksum" - cof "$1" > .globalignored/"$1".checksum -} +mkdir -p .globalignored +"$(dirname "$0")/cof" "$1" > ".globalignored/$1.checksum" diff --git a/bin/local/README.md b/bin/local/README.md new file mode 100644 index 0000000..e37ec2c --- /dev/null +++ b/bin/local/README.md @@ -0,0 +1,5 @@ +# Local scripts + +Scripts in this folder are on PATH (the install script adds it) but never +committed: everything here except this README is gitignored. Use it for +machine- or work-specific scripts that do not belong in a private repo. diff --git a/bin/mci b/bin/mci index 7d41371..31e7fcd 100755 --- a/bin/mci +++ b/bin/mci @@ -1,17 +1,18 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then +if [ "$1" = "--help" ]; then echo "Performs a Maven clean install without tests and updates the pom.xml checksum" exit 0 fi -JAVA_VERSION=$(grep java.version pom.xml | sed 's/[^0-9]//g') -echo "Detected java version: $JAVA_VERSION" - -JAVA_HOME=$(/usr/libexec/java_home -v "$JAVA_VERSION") -export JAVA_HOME +JAVA_VERSION=$(sed -n 's|.*[[:space:]]*\([0-9][0-9.]*\).*|\1|p' pom.xml | head -n 1) +if [ -n "$JAVA_VERSION" ] && [ -x /usr/libexec/java_home ]; then + echo "Detected java version: $JAVA_VERSION" + JAVA_HOME=$(/usr/libexec/java_home -v "$JAVA_VERSION") + export JAVA_HOME +else + echo "Could not detect the java version from pom.xml, using the default java" +fi mvn clean install -Dmaven.test.skip=true -Ddependency-check.skip=true -# shellcheck source=/dev/null -source "${BASH_SOURCE%/*}/lib/save_checksum" -save_checksum pom.xml +"$(dirname "$0")/lib/save_checksum" pom.xml diff --git a/bin/sb b/bin/sb index 2b4b4b7..3b23fb9 100755 --- a/bin/sb +++ b/bin/sb @@ -1,38 +1,32 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then +if [ "$1" = "--help" ]; then echo "Start a spring boot server" echo "Does a maven clean install if the pom.xml changed" echo " --pull to git pull before starting" exit 0 fi -# Read arguments -for arg in "$@" -do - case $arg in +for arg in "$@"; do + case "$arg" in --pull) - SHOULD_PULL=1 - shift - ;; + echo 'Pulling' + git pull + ;; esac done -if [[ $SHOULD_PULL = "1" ]] ; then - echo 'Pulling' - git pull -fi - -# shellcheck source=/dev/null -source "${BASH_SOURCE%/*}/lib/checksum_changed" -if checksum_changed pom.xml; then +if "$(dirname "$0")/lib/checksum_changed" pom.xml; then echo 'pom.xml changed; Running "mvn install -Dmaven.test.skip=true"' - mci + "$(dirname "$0")/mci" fi -JAVA_VERSION=$(grep java.version pom.xml | sed 's/[^0-9]//g') -echo "Detected java version: $JAVA_VERSION" - -JAVA_HOME=$(/usr/libexec/java_home -v "$JAVA_VERSION") -export JAVA_HOME +JAVA_VERSION=$(sed -n 's|.*[[:space:]]*\([0-9][0-9.]*\).*|\1|p' pom.xml | head -n 1) +if [ -n "$JAVA_VERSION" ] && [ -x /usr/libexec/java_home ]; then + echo "Detected java version: $JAVA_VERSION" + JAVA_HOME=$(/usr/libexec/java_home -v "$JAVA_VERSION") + export JAVA_HOME +else + echo "Could not detect the java version from pom.xml, using the default java" +fi mvn clean spring-boot:run -Dmaven.test.skip=true diff --git a/bin/sq b/bin/sq new file mode 100755 index 0000000..086edcd --- /dev/null +++ b/bin/sq @@ -0,0 +1,108 @@ +#!/bin/sh + +usage() { + cat <<'EOF' +Usage: sq + +Squashes all commits on the current branch into a single commit with + as its commit message. The squashed commits are those not on +the base branch: the remote default branch when known, otherwise the +first of master, main, develop or development that exists. + +When the branch has an upstream, the squashed branch is pushed with +'git push --force-with-lease' afterwards. + +Refuses to run with uncommitted changes, on the base branch itself, or on +a detached HEAD. Also refuses when another local branch points at a commit +between the base and HEAD (a stacked branch), since squashing would absorb +that branch's commits. +EOF +} + +case "$1" in + -h|--help) + usage + exit 0 + ;; + '') + usage >&2 + exit 1 + ;; +esac + +message=$1 + +current=$(git rev-parse --abbrev-ref HEAD) || exit 1 +if [ "$current" = "HEAD" ]; then + echo "Not on a branch (detached HEAD), nothing to squash" >&2 + exit 1 +fi + +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "You have uncommitted changes, commit or stash them first" >&2 + exit 1 +fi + +# The remote default branch, e.g. origin/main. +base='' +for remote in $(git remote); do + if ref=$(git symbolic-ref --quiet "refs/remotes/$remote/HEAD"); then + base=${ref#refs/remotes/} + break + fi +done +if [ -z "$base" ]; then + for name in master main develop development; do + if git rev-parse --verify --quiet "refs/heads/$name" >/dev/null; then + base=$name + break + fi + done +fi +if [ -z "$base" ]; then + echo "Could not determine the base branch" >&2 + exit 1 +fi + +if [ "$current" = "${base#*/}" ]; then + echo "On base branch '$current', refusing to squash it" >&2 + exit 1 +fi + +merge_base=$(git merge-base "$base" HEAD) || exit 1 + +count=$(git rev-list --count "$merge_base..HEAD") +if [ "$count" -lt 2 ]; then + echo "Nothing to squash: $count commit(s) on '$current' since '$base'" + exit 0 +fi + +# A local branch tip strictly between the merge base and HEAD means the +# current branch is stacked on it; squashing would absorb its commits. +head=$(git rev-parse HEAD) +stacked=$(git for-each-ref --format='%(refname:short)' refs/heads | + while read -r branch; do + [ "$branch" = "$current" ] && continue + tip=$(git rev-parse "refs/heads/$branch") + [ "$tip" = "$merge_base" ] && continue + [ "$tip" = "$head" ] && continue + if git merge-base --is-ancestor "$merge_base" "$tip" && + git merge-base --is-ancestor "$tip" "$head"; then + echo "$branch" + fi + done) +if [ -n "$stacked" ]; then + echo "Refusing to squash: '$current' is stacked on top of:" >&2 + printf '%s\n' "$stacked" | sed 's/^/ /' >&2 + echo "Squashing down to '$base' would absorb those branches' commits" >&2 + exit 1 +fi + +echo "Squashing $count commits on '$current' since '$base' into one" +git reset --soft "$merge_base" && + git commit -m "$message" || exit 1 + +if git rev-parse --verify --quiet "$current@{u}" >/dev/null 2>&1; then + echo "Pushing '$current' with --force-with-lease" + git push --force-with-lease +fi diff --git a/bin/sw b/bin/sw index be0d415..05f08fa 100755 --- a/bin/sw +++ b/bin/sw @@ -1,16 +1,70 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then - echo "Tries to find a project under ~/Source and opens it in VScode." - echo "TODO: allow for other IDEs to work as well" - exit 0 +usage() { + cat <<'EOF' +Usage: sw + +Finds a project folder under $PROJECTS_DIR (default: ~/Projects) and opens +it in VSCode. Folders are searched for directly under the projects folder +and one level deeper. is matched against folder names as a +case-insensitive extended regex; whole-name matches win over partial +matches, and direct children win over deeper folders. + +If multiple folders match equally well, all matches are listed and nothing +is opened. +EOF +} + +case "$1" in + ''|-h|--help) + usage + exit 0 + ;; +esac + +name=$1 +projects=${PROJECTS_DIR:-$HOME/Projects} + +if [ ! -d "$projects" ]; then + echo "Projects folder '$projects' does not exist" >&2 + exit 1 fi -set -e +if ! command -v code > /dev/null 2>&1; then + echo "VSCode's 'code' command not found on PATH" >&2 + exit 1 +fi + +# Opens a single match in VSCode, or lists them all when ambiguous. +open_match() { + matches=$1 + + [ -n "$matches" ] || return 1 + + count=$(printf '%s\n' "$matches" | wc -l | tr -d ' ') + if [ "$count" -gt 1 ]; then + echo "'$name' matches multiple projects:" >&2 + printf '%s\n' "$matches" | sed 's/^/ /' >&2 + exit 1 + fi + + echo "Opening $matches" + code "$matches" + exit $? +} -# shellcheck source=/dev/null -source "${BASH_SOURCE%/*}/lib/project_root" +# Lists folders between the given depths whose name matches the regex. +match_projects() { + find -L "$projects" -mindepth "$1" -maxdepth "$2" \ + -type d ! -path '*/.*' 2>/dev/null | + while read -r dir; do + basename "$dir" | grep -iqE "$3" && echo "$dir" + done +} -project_location=$(project_root "$1") +open_match "$(match_projects 1 2 "^($name)\$")" +open_match "$(match_projects 1 1 "$name")" +open_match "$(match_projects 2 2 "$name")" -(cd "$project_location" && code .) +echo "No project matching '$name' found under $projects" >&2 +exit 1 diff --git a/bin/wip b/bin/wip index 709b211..8b47ef3 100755 --- a/bin/wip +++ b/bin/wip @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if [[ $* == *"--help"* ]]; then +if [ "$1" = "--help" ]; then echo "Quickly commits and current changes under a 'WIP' message." exit 0 fi diff --git a/bin/wsldockerd b/bin/wsldockerd deleted file mode 100755 index 8bab854..0000000 --- a/bin/wsldockerd +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# if [[ $* == *"--help"* ]]; then -# echo "Starts Docker Daemon on WSL (because lack of systemd)" -# exit 0 -# fi - -# DOCKER_DISTRO="Ubuntu" -# DOCKER_DIR=/mnt/wsl/shared-docker -# DOCKER_SOCK="$DOCKER_DIR/docker.sock" -# export DOCKER_HOST="unix://$DOCKER_SOCK" -# if [ ! -S "$DOCKER_SOCK" ]; then -# mkdir -p "$DOCKER_DIR" -# chmod 755 "$DOCKER_DIR" -# chgrp docker "$DOCKER_DIR" -# /mnt/c/Windows/System32/wsl.exe -d $DOCKER_DISTRO sh -c "nohup sudo -b dockerd < /dev/null > $DOCKER_DIR/dockerd.log 2>&1" -# fi diff --git a/claude.md b/claude.md new file mode 100644 index 0000000..6e89c3c --- /dev/null +++ b/claude.md @@ -0,0 +1 @@ +All scripts must be POSIX compatible. And contain a --help flag that explains what the script does diff --git a/config/ssh/config b/config/ssh/config deleted file mode 100644 index 884dc0b..0000000 --- a/config/ssh/config +++ /dev/null @@ -1,6 +0,0 @@ -Host github.com - User git -Host *amazonaws.com - User ec2-user -Host rpi.jor.dev - User alarm diff --git a/install b/install new file mode 100755 index 0000000..ef975ad --- /dev/null +++ b/install @@ -0,0 +1,48 @@ +#!/bin/sh + +usage() { + cat <<'EOF' +Usage: ./install [rc-file] + +Installs these scripts into your shell rc file: adds bin/ and bin/local/ +to PATH and sources every file under profile/ and aliasses/. + +[rc-file] defaults to ~/.zshrc when your shell is zsh, ~/.bashrc when it +is bash, and ~/.profile otherwise. Running the script again is a no-op: +the block is only added once. +EOF +} + +case "$1" in + -h|--help) + usage + exit 0 + ;; +esac + +scripts_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) + +rc=$1 +if [ -z "$rc" ]; then + case "${SHELL##*/}" in + zsh) rc=$HOME/.zshrc ;; + bash) rc=$HOME/.bashrc ;; + *) rc=$HOME/.profile ;; + esac +fi + +marker="# Jor Scripts" +if [ -f "$rc" ] && grep -qF "$marker" "$rc"; then + echo "Already installed in $rc" + exit 0 +fi + +cat >> "$rc" < /dev/null || ssh-add "$HOME"/.ssh/*.key +if ! ssh-add -l > /dev/null 2>&1 && ls "$HOME"/.ssh/*.key > /dev/null 2>&1; then + ssh-add "$HOME"/.ssh/*.key +fi diff --git a/profile/keychain-rpi b/profile/keychain-rpi index 0b9c167..3e4bdbc 100755 --- a/profile/keychain-rpi +++ b/profile/keychain-rpi @@ -1,18 +1,18 @@ -#!/bin/bash +#!/bin/sh -if ! hostname | grep -iqE 'raspberry' &> /dev/null ; then +if ! hostname | grep -iq 'raspberry'; then return fi -if ls ~/.keychain/*-sh &> /dev/null ; then - keychain --agents ssh &> /dev/null +if ls ~/.keychain/*-sh > /dev/null 2>&1; then + keychain --agents ssh > /dev/null 2>&1 # shellcheck source=/dev/null - source ~/.keychain/*-sh + . ~/.keychain/*-sh fi -#ssh-add -l &> /dev/null || ssh-add "$HOME"/.ssh/*.key +#ssh-add -l > /dev/null 2>&1 || ssh-add "$HOME"/.ssh/*.key -if ! pgrep gpg-agent &> /dev/null ; then +if ! pgrep gpg-agent > /dev/null 2>&1; then GPG_TTY=$(tty) export GPG_TTY # shellcheck disable=SC2046 diff --git a/profile/keychain-wsl b/profile/keychain-wsl index 7b7cfe0..3057bf0 100755 --- a/profile/keychain-wsl +++ b/profile/keychain-wsl @@ -1,18 +1,20 @@ -#!/bin/bash +#!/bin/sh -if ! which powershell.exe &> /dev/null ; then +if ! command -v powershell.exe > /dev/null 2>&1; then return fi -if ls ~/.keychain/*-sh &> /dev/null ; then - keychain --agents ssh &> /dev/null +if ls ~/.keychain/*-sh > /dev/null 2>&1; then + keychain --agents ssh > /dev/null 2>&1 # shellcheck source=/dev/null - source ~/.keychain/*-sh + . ~/.keychain/*-sh fi -ssh-add -l &> /dev/null || ssh-add "$HOME"/.ssh/*.key +if ! ssh-add -l > /dev/null 2>&1 && ls "$HOME"/.ssh/*.key > /dev/null 2>&1; then + ssh-add "$HOME"/.ssh/*.key +fi -if ! pgrep gpg-agent &> /dev/null ; then +if ! pgrep gpg-agent > /dev/null 2>&1; then GPG_TTY=$(tty) export GPG_TTY # shellcheck disable=SC2046 diff --git a/profile/ksops b/profile/ksops index c043ffb..2e8f403 100755 --- a/profile/ksops +++ b/profile/ksops @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh -export XDG_CONFIG_HOME=$HOME/.config +export XDG_CONFIG_HOME="$HOME"/.config diff --git a/profile/kubectl b/profile/kubectl index 920e525..0111616 100755 --- a/profile/kubectl +++ b/profile/kubectl @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh -export KUBECONFIG=$HOME/.kube/config.yaml +export KUBECONFIG="$HOME"/.kube/config.yaml diff --git a/profile/less b/profile/less index 70efefe..a839d2d 100755 --- a/profile/less +++ b/profile/less @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh export LESS="-eirMFX" diff --git a/profile/nvm b/profile/nvm index 3abe594..8afa690 100755 --- a/profile/nvm +++ b/profile/nvm @@ -1,27 +1,23 @@ -#!/bin/bash +#!/bin/sh -if ! test -f "/opt/homebrew/opt/nvm/nvm.sh" ; then +if [ ! -f "/opt/homebrew/opt/nvm/nvm.sh" ]; then return fi export NVM_DIR="$HOME/.nvm" # shellcheck source=/dev/null -[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh" # This loads nvm +[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && . "/opt/homebrew/opt/nvm/nvm.sh" # This loads nvm # shellcheck source=/dev/null -[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion +[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && . "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion - -# Auto load correct Node version -autoload -U add-zsh-hook -load-nvmrc() { - # shellcheck disable=SC2155 - local node_version="$(nvm version)" - # shellcheck disable=SC2155 - local nvmrc_path="$(nvm_find_nvmrc)" +# Auto load correct Node version on directory change; the hook is a zsh +# feature, so only register it there. +load_nvmrc() { + node_version="$(nvm version)" + nvmrc_path="$(nvm_find_nvmrc)" if [ -n "$nvmrc_path" ]; then - # shellcheck disable=SC2155 - local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")") + nvmrc_node_version=$(nvm version "$(cat "$nvmrc_path")") if [ "$nvmrc_node_version" = "N/A" ]; then nvm install @@ -33,5 +29,9 @@ load-nvmrc() { nvm use default fi } -add-zsh-hook chpwd load-nvmrc -load-nvmrc + +if [ -n "$ZSH_VERSION" ]; then + autoload -U add-zsh-hook + add-zsh-hook chpwd load_nvmrc +fi +load_nvmrc diff --git a/profile/projects b/profile/projects new file mode 100755 index 0000000..d42fbfb --- /dev/null +++ b/profile/projects @@ -0,0 +1,3 @@ +#!/bin/sh + +export PROJECTS_DIR="$HOME/Projects" diff --git a/profile/pyenv b/profile/pyenv index e28e254..68f9851 100755 --- a/profile/pyenv +++ b/profile/pyenv @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if ! ls "$HOME/.pyenv" &> /dev/null ; then +if [ ! -d "$HOME/.pyenv" ]; then return fi diff --git a/profile/sentry b/profile/sentry index 89afefe..a540757 100755 --- a/profile/sentry +++ b/profile/sentry @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh export SENTRY_IGNORE_API_RESOLUTION_ERROR=1 diff --git a/profile/vim b/profile/vim index 601f070..5c712bd 100755 --- a/profile/vim +++ b/profile/vim @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh export EDITOR=vim diff --git a/profile/wsl b/profile/wsl index 3c87c49..625ef12 100755 --- a/profile/wsl +++ b/profile/wsl @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh -if ! which powershell.exe &> /dev/null ; then +if ! command -v powershell.exe > /dev/null 2>&1; then return fi From f00d7574a4d785125409025f48ebaa1fa7fa61e0 Mon Sep 17 00:00:00 2001 From: Jor Sanders Date: Sat, 25 Jul 2026 03:21:57 +0200 Subject: [PATCH 2/5] fix: satisfy super-linter v8 - Pin actions to SHAs and add minimal workflow permissions (checkov, zizmor) - Add jscpd threshold config - shfmt all scripts, add .editorconfig - Markdown and terminology fixes (prettier, markdownlint, textlint) Co-Authored-By: Claude Fable 5 --- .editorconfig | 11 +++++++++++ .github/linters/.jscpd.json | 3 +++ .github/workflows/CI-CD.yml | 9 +++++++-- README.md | 6 ++++-- bin/cb | 2 +- bin/ec | 2 +- bin/gc | 8 ++++---- bin/lib/save_checksum | 2 +- bin/local/README.md | 5 +++-- bin/sq | 2 +- bin/sw | 4 ++-- claude.md | 5 ++++- install | 8 ++++---- profile/keybinds-osx | 6 +++--- profile/keychain-osx | 2 +- profile/keychain-rpi | 6 +++--- profile/keychain-wsl | 10 +++++----- profile/nvm | 28 ++++++++++++++-------------- profile/wsl | 2 +- 19 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/linters/.jscpd.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..39c5277 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +switch_case_indent = true + +[*.{yml,yaml,json,md}] +indent_size = 2 diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..33c7766 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,3 @@ +{ + "threshold": 5 +} diff --git a/.github/workflows/CI-CD.yml b/.github/workflows/CI-CD.yml index 51b2b94..353c6a2 100644 --- a/.github/workflows/CI-CD.yml +++ b/.github/workflows/CI-CD.yml @@ -3,16 +3,21 @@ name: CI-CD on: [push] +permissions: + contents: read + packages: read + statuses: write + jobs: lint: name: Linters runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Run Super-Linter - uses: super-linter/super-linter/slim@v8 + uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 46cfec6..b2a9306 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ # Scripts + These are some non-secret scripts/configs I use locally to develop. Since I tend to break things, or redo my local setup I place them in version control ## Fair warning -I highly recommend not running other people commands blindly on your machine. So I don't expect others to do the same with mine. Which is why the only documentation included; is meant for myself. +I highly recommend not running other people commands blindly on your machine. So I don't expect others to do the same with mine. Which is why the only documentation included; is meant for myself. ## Setup Run the install script. It adds bin/ and bin/local/ (gitignored, machine-local scripts) to PATH and sources every file under profile/ and -aliasses/ from your shell rc file (~/.zshrc for zsh, ~/.bashrc for bash), +aliasses/ from your shell rc file (~/.zshrc for Zsh, ~/.bashrc for Bash), see `./install --help`: + ```shell ./install ``` diff --git a/bin/cb b/bin/cb index a5bde2b..748524b 100755 --- a/bin/cb +++ b/bin/cb @@ -14,7 +14,7 @@ EOF } case "$1" in - -h|--help) + -h | --help) usage exit 0 ;; diff --git a/bin/ec b/bin/ec index 3f86df9..45b004d 100755 --- a/bin/ec +++ b/bin/ec @@ -12,7 +12,7 @@ EOF } case "$1" in - -h|--help) + -h | --help) usage exit 0 ;; diff --git a/bin/gc b/bin/gc index a7a555f..e52c3d0 100755 --- a/bin/gc +++ b/bin/gc @@ -33,7 +33,7 @@ EOF } case "$1" in - ''|-h|--help) + '' | -h | --help) usage exit 0 ;; @@ -98,9 +98,9 @@ checkout_match() { # Returns the well-known counterpart of a branch name, if any. alias_for() { case "$1" in - master) echo main ;; - main) echo master ;; - develop) echo development ;; + master) echo main ;; + main) echo master ;; + develop) echo development ;; development) echo develop ;; esac } diff --git a/bin/lib/save_checksum b/bin/lib/save_checksum index d0a58c4..7a175e3 100755 --- a/bin/lib/save_checksum +++ b/bin/lib/save_checksum @@ -8,4 +8,4 @@ if [ $# -lt 1 ] || [ "$1" = "--help" ]; then fi mkdir -p .globalignored -"$(dirname "$0")/cof" "$1" > ".globalignored/$1.checksum" +"$(dirname "$0")/cof" "$1" >".globalignored/$1.checksum" diff --git a/bin/local/README.md b/bin/local/README.md index e37ec2c..6299772 100644 --- a/bin/local/README.md +++ b/bin/local/README.md @@ -1,5 +1,6 @@ # Local scripts Scripts in this folder are on PATH (the install script adds it) but never -committed: everything here except this README is gitignored. Use it for -machine- or work-specific scripts that do not belong in a private repo. +committed: everything here except this file is gitignored. Use it for +machine- or work-specific scripts that do not belong in a private +repository. diff --git a/bin/sq b/bin/sq index 086edcd..a0f1a14 100755 --- a/bin/sq +++ b/bin/sq @@ -20,7 +20,7 @@ EOF } case "$1" in - -h|--help) + -h | --help) usage exit 0 ;; diff --git a/bin/sw b/bin/sw index 05f08fa..6726332 100755 --- a/bin/sw +++ b/bin/sw @@ -16,7 +16,7 @@ EOF } case "$1" in - ''|-h|--help) + '' | -h | --help) usage exit 0 ;; @@ -30,7 +30,7 @@ if [ ! -d "$projects" ]; then exit 1 fi -if ! command -v code > /dev/null 2>&1; then +if ! command -v code >/dev/null 2>&1; then echo "VSCode's 'code' command not found on PATH" >&2 exit 1 fi diff --git a/claude.md b/claude.md index 6e89c3c..64d6552 100644 --- a/claude.md +++ b/claude.md @@ -1 +1,4 @@ -All scripts must be POSIX compatible. And contain a --help flag that explains what the script does +# Claude instructions + +All scripts must be POSIX compatible. And contain a --help flag that +explains what the script does diff --git a/install b/install index ef975ad..fb23f94 100755 --- a/install +++ b/install @@ -14,7 +14,7 @@ EOF } case "$1" in - -h|--help) + -h | --help) usage exit 0 ;; @@ -25,9 +25,9 @@ scripts_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) rc=$1 if [ -z "$rc" ]; then case "${SHELL##*/}" in - zsh) rc=$HOME/.zshrc ;; + zsh) rc=$HOME/.zshrc ;; bash) rc=$HOME/.bashrc ;; - *) rc=$HOME/.profile ;; + *) rc=$HOME/.profile ;; esac fi @@ -37,7 +37,7 @@ if [ -f "$rc" ] && grep -qF "$marker" "$rc"; then exit 0 fi -cat >> "$rc" <>"$rc" < /dev/null 2>&1 && ls "$HOME"/.ssh/*.key > /dev/null 2>&1; then +if ! ssh-add -l >/dev/null 2>&1 && ls "$HOME"/.ssh/*.key >/dev/null 2>&1; then ssh-add "$HOME"/.ssh/*.key fi diff --git a/profile/keychain-rpi b/profile/keychain-rpi index 3e4bdbc..9394e28 100755 --- a/profile/keychain-rpi +++ b/profile/keychain-rpi @@ -4,15 +4,15 @@ if ! hostname | grep -iq 'raspberry'; then return fi -if ls ~/.keychain/*-sh > /dev/null 2>&1; then - keychain --agents ssh > /dev/null 2>&1 +if ls ~/.keychain/*-sh >/dev/null 2>&1; then + keychain --agents ssh >/dev/null 2>&1 # shellcheck source=/dev/null . ~/.keychain/*-sh fi #ssh-add -l > /dev/null 2>&1 || ssh-add "$HOME"/.ssh/*.key -if ! pgrep gpg-agent > /dev/null 2>&1; then +if ! pgrep gpg-agent >/dev/null 2>&1; then GPG_TTY=$(tty) export GPG_TTY # shellcheck disable=SC2046 diff --git a/profile/keychain-wsl b/profile/keychain-wsl index 3057bf0..4351b29 100755 --- a/profile/keychain-wsl +++ b/profile/keychain-wsl @@ -1,20 +1,20 @@ #!/bin/sh -if ! command -v powershell.exe > /dev/null 2>&1; then +if ! command -v powershell.exe >/dev/null 2>&1; then return fi -if ls ~/.keychain/*-sh > /dev/null 2>&1; then - keychain --agents ssh > /dev/null 2>&1 +if ls ~/.keychain/*-sh >/dev/null 2>&1; then + keychain --agents ssh >/dev/null 2>&1 # shellcheck source=/dev/null . ~/.keychain/*-sh fi -if ! ssh-add -l > /dev/null 2>&1 && ls "$HOME"/.ssh/*.key > /dev/null 2>&1; then +if ! ssh-add -l >/dev/null 2>&1 && ls "$HOME"/.ssh/*.key >/dev/null 2>&1; then ssh-add "$HOME"/.ssh/*.key fi -if ! pgrep gpg-agent > /dev/null 2>&1; then +if ! pgrep gpg-agent >/dev/null 2>&1; then GPG_TTY=$(tty) export GPG_TTY # shellcheck disable=SC2046 diff --git a/profile/nvm b/profile/nvm index 8afa690..e8e1b36 100755 --- a/profile/nvm +++ b/profile/nvm @@ -6,28 +6,28 @@ fi export NVM_DIR="$HOME/.nvm" # shellcheck source=/dev/null -[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && . "/opt/homebrew/opt/nvm/nvm.sh" # This loads nvm +[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && . "/opt/homebrew/opt/nvm/nvm.sh" # This loads nvm # shellcheck source=/dev/null -[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && . "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion +[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && . "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion # Auto load correct Node version on directory change; the hook is a zsh # feature, so only register it there. load_nvmrc() { - node_version="$(nvm version)" - nvmrc_path="$(nvm_find_nvmrc)" + node_version="$(nvm version)" + nvmrc_path="$(nvm_find_nvmrc)" - if [ -n "$nvmrc_path" ]; then - nvmrc_node_version=$(nvm version "$(cat "$nvmrc_path")") + if [ -n "$nvmrc_path" ]; then + nvmrc_node_version=$(nvm version "$(cat "$nvmrc_path")") - if [ "$nvmrc_node_version" = "N/A" ]; then - nvm install - elif [ "$nvmrc_node_version" != "$node_version" ]; then - nvm use + if [ "$nvmrc_node_version" = "N/A" ]; then + nvm install + elif [ "$nvmrc_node_version" != "$node_version" ]; then + nvm use + fi + elif [ "$node_version" != "$(nvm version default)" ]; then + echo "Reverting to nvm default version" + nvm use default fi - elif [ "$node_version" != "$(nvm version default)" ]; then - echo "Reverting to nvm default version" - nvm use default - fi } if [ -n "$ZSH_VERSION" ]; then diff --git a/profile/wsl b/profile/wsl index 625ef12..2c6a23b 100755 --- a/profile/wsl +++ b/profile/wsl @@ -1,6 +1,6 @@ #!/bin/sh -if ! command -v powershell.exe > /dev/null 2>&1; then +if ! command -v powershell.exe >/dev/null 2>&1; then return fi From 8573dfa8ee9ac5709d717dceb8a78cfc4a6c9da0 Mon Sep 17 00:00:00 2001 From: Jor Sanders Date: Sat, 25 Jul 2026 03:26:48 +0200 Subject: [PATCH 3/5] chore: empty commit From 52d1bb5cf9fe12863b5ae2c6d87ec1a76af58884 Mon Sep 17 00:00:00 2001 From: Jor Sanders Date: Sat, 25 Jul 2026 03:27:38 +0200 Subject: [PATCH 4/5] fix: remaining super-linter findings - persist-credentials: false on checkout (zizmor artipacked) - Disable Biome formatter, it conflicts with Prettier on JSON - 4-space indent in gc help text (editorconfig-checker) - codespell config ignoring 'aliasses' Co-Authored-By: Claude Fable 5 --- .codespellrc | 3 +++ .github/workflows/CI-CD.yml | 3 +++ bin/gc | 18 +++++++++--------- 3 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 .codespellrc diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..af2e7d9 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +; aliasses is the intentional (if creative) name of the aliasses/ folder +ignore-words-list = aliasses diff --git a/.github/workflows/CI-CD.yml b/.github/workflows/CI-CD.yml index 353c6a2..6cf652c 100644 --- a/.github/workflows/CI-CD.yml +++ b/.github/workflows/CI-CD.yml @@ -17,7 +17,10 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - name: Run Super-Linter uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Biome's JSON formatting conflicts with Prettier's; keep Prettier + VALIDATE_BIOME_FORMAT: false diff --git a/bin/gc b/bin/gc index e52c3d0..cf421cf 100755 --- a/bin/gc +++ b/bin/gc @@ -12,15 +12,15 @@ exist but its counterpart does: master <-> main, develop <-> development. So 'gc master' checks out 'main' in a repo that only has 'main'. Search order: - 1. Exact tag - 2. Exact local branch (followed by a git pull) - 3. Remote-prefixed branch, e.g. 'origin/my-branch' checks out 'my-branch' - 4. Exact branch on any remote (checked out as a tracking branch) - 5. Any other rev, e.g. a commit hash (detached HEAD) - 6. Partial match on local branch names - 7. Partial match on remote branch names (matched against 'remote/branch') - 8. Partial match on tag names - 9. Nothing matched: a new branch named is created + 1. Exact tag + 2. Exact local branch (followed by a git pull) + 3. Remote-prefixed branch, e.g. 'origin/my-branch' checks out 'my-branch' + 4. Exact branch on any remote (checked out as a tracking branch) + 5. Any other rev, e.g. a commit hash (detached HEAD) + 6. Partial match on local branch names + 7. Partial match on remote branch names (matched against 'remote/branch') + 8. Partial match on tag names + 9. Nothing matched: a new branch named is created A local branch that is checked out in another worktree (locked or not) has that worktree removed before being checked out here. A worktree holding From f799e2e02f9d6f665329bc04b2e65a8c70832fdb Mon Sep 17 00:00:00 2001 From: Jor Sanders Date: Sat, 25 Jul 2026 03:28:51 +0200 Subject: [PATCH 5/5] fix: rename aliasses folder to aliases Co-Authored-By: Claude Fable 5 --- .codespellrc | 3 --- README.md | 2 +- {aliasses => aliases}/git | 0 {aliasses => aliases}/kubectl | 0 {aliasses => aliases}/minikube | 0 {aliasses => aliases}/utils | 0 {aliasses => aliases}/vim | 0 install | 4 ++-- 8 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 .codespellrc rename {aliasses => aliases}/git (100%) rename {aliasses => aliases}/kubectl (100%) rename {aliasses => aliases}/minikube (100%) rename {aliasses => aliases}/utils (100%) rename {aliasses => aliases}/vim (100%) diff --git a/.codespellrc b/.codespellrc deleted file mode 100644 index af2e7d9..0000000 --- a/.codespellrc +++ /dev/null @@ -1,3 +0,0 @@ -[codespell] -; aliasses is the intentional (if creative) name of the aliasses/ folder -ignore-words-list = aliasses diff --git a/README.md b/README.md index b2a9306..6bcfd47 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ I highly recommend not running other people commands blindly on your machine. So Run the install script. It adds bin/ and bin/local/ (gitignored, machine-local scripts) to PATH and sources every file under profile/ and -aliasses/ from your shell rc file (~/.zshrc for Zsh, ~/.bashrc for Bash), +aliases/ from your shell rc file (~/.zshrc for Zsh, ~/.bashrc for Bash), see `./install --help`: ```shell diff --git a/aliasses/git b/aliases/git similarity index 100% rename from aliasses/git rename to aliases/git diff --git a/aliasses/kubectl b/aliases/kubectl similarity index 100% rename from aliasses/kubectl rename to aliases/kubectl diff --git a/aliasses/minikube b/aliases/minikube similarity index 100% rename from aliasses/minikube rename to aliases/minikube diff --git a/aliasses/utils b/aliases/utils similarity index 100% rename from aliasses/utils rename to aliases/utils diff --git a/aliasses/vim b/aliases/vim similarity index 100% rename from aliasses/vim rename to aliases/vim diff --git a/install b/install index fb23f94..bc3ffbd 100755 --- a/install +++ b/install @@ -5,7 +5,7 @@ usage() { Usage: ./install [rc-file] Installs these scripts into your shell rc file: adds bin/ and bin/local/ -to PATH and sources every file under profile/ and aliasses/. +to PATH and sources every file under profile/ and aliases/. [rc-file] defaults to ~/.zshrc when your shell is zsh, ~/.bashrc when it is bash, and ~/.profile otherwise. Running the script again is a no-op: @@ -42,7 +42,7 @@ cat >>"$rc" <