From c7d71df4ab7c03c8a09ee8f5307f29f1a8ccdd09 Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Mon, 26 Oct 2026 12:43:12 +0100 Subject: [PATCH] scripts, build, just: POSIX Compliance Enforce POSIX shell syntax with ShellCheck using `--shell sh`. --- build/build | 2 +- build/checksums | 2 +- build/release | 2 +- build/rust-version | 2 +- build/tag | 2 +- build/upload | 2 +- build/version | 2 +- flake.nix | 5 +++- justfile | 2 +- scripts/build-man-pages.sh | 2 +- scripts/cache-cobs | 2 +- scripts/changelog | 2 +- scripts/clear-refs-db.sh | 2 +- scripts/create-env.sh | 2 +- scripts/delete-remote-branches.sh | 2 +- scripts/git-hook-template.sh | 30 ++++++++++----------- scripts/import-issue.sh | 43 +++++++++++-------------------- scripts/just/check-ellipses.sh | 40 ++++++++++++++++------------ scripts/just/check-hooks.sh | 14 +++++----- scripts/just/check-keywords.sh | 2 +- scripts/just/commit-msg.sh | 2 +- scripts/just/format-nix.sh | 2 +- scripts/just/install-hooks.sh | 7 ++--- scripts/just/verify-tool.sh | 2 +- scripts/patch-rebase.sh | 2 +- scripts/show-sigrefs.sh | 9 +++++-- 26 files changed, 94 insertions(+), 92 deletions(-) diff --git a/build/build b/build/build index 4d48c7d55..7a83506b2 100755 --- a/build/build +++ b/build/build @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e main() { diff --git a/build/checksums b/build/checksums index a0225cf56..5549e85db 100755 --- a/build/checksums +++ b/build/checksums @@ -1,2 +1,2 @@ -#!/bin/sh +#! /usr/bin/env sh find build/artifacts -type f -name '*.sha256' -exec cat {} + diff --git a/build/release b/build/release index f16011c33..da67429cd 100755 --- a/build/release +++ b/build/release @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e if [ $# -ne 1 ]; then diff --git a/build/rust-version b/build/rust-version index e500392df..218b91685 100755 --- a/build/rust-version +++ b/build/rust-version @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -euo pipefail if ! version=$(grep -m 1 -oP '^\s*channel\s*=\s*"?\K([\d\.]+)' rust-toolchain.toml) diff --git a/build/tag b/build/tag index 82c38ff52..6a6a03010 100755 --- a/build/tag +++ b/build/tag @@ -1,4 +1,4 @@ -#! /bin/sh +#! /usr/bin/env sh set -e if [ $# -ne 1 ]; then diff --git a/build/upload b/build/upload index bed1533cb..69ce4d4a0 100755 --- a/build/upload +++ b/build/upload @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e SSH_LOGIN=${SSH_LOGIN:-release} diff --git a/build/version b/build/version index b1de9c37d..83c2557e5 100755 --- a/build/version +++ b/build/version @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh if ! version="$(git describe --match='releases/*' --candidates=1 2>/dev/null)"; then echo "fatal: no version tag found by 'git describe'" >&2 ; exit 1 diff --git a/flake.nix b/flake.nix index b7e60f2fd..950026533 100644 --- a/flake.nix +++ b/flake.nix @@ -294,7 +294,10 @@ clippy = rustup.toolchain; }; }; - shellcheck.enable = true; + shellcheck = { + enable = true; + args = ["--shell" "sh" "--exclude" "SC3040"]; + }; } // grep.hooks; }; diff --git a/justfile b/justfile index b2f3cd173..f33cf2377 100644 --- a/justfile +++ b/justfile @@ -90,7 +90,7 @@ check-spelling: (verify-tool "codespell") [parallel] check-scripts: (verify-tool "shellcheck") @echo "{{CHECK}}Checking shell scripts…{{NORMAL}}" - @find . -type f -name "*.sh" -exec shellcheck {} + + @find . -type f -name "*.sh" -exec shellcheck --shell sh --exclude SC3040 {} + # Run checks for forbidden keywords [group('pre-commit')] diff --git a/scripts/build-man-pages.sh b/scripts/build-man-pages.sh index c05073f2d..e9c5a9bb5 100755 --- a/scripts/build-man-pages.sh +++ b/scripts/build-man-pages.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e diff --git a/scripts/cache-cobs b/scripts/cache-cobs index b29013ac0..3916e94aa 100755 --- a/scripts/cache-cobs +++ b/scripts/cache-cobs @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e DIRECTORY="$(rad path)/storage" diff --git a/scripts/changelog b/scripts/changelog index 4451a5113..37d57fe99 100755 --- a/scripts/changelog +++ b/scripts/changelog @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e render_commit_footnotes() { diff --git a/scripts/clear-refs-db.sh b/scripts/clear-refs-db.sh index 32fb3fd95..1f989d57e 100755 --- a/scripts/clear-refs-db.sh +++ b/scripts/clear-refs-db.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e DB="$(rad path)/node/node.db" diff --git a/scripts/create-env.sh b/scripts/create-env.sh index ff4674806..6fecc78f7 100755 --- a/scripts/create-env.sh +++ b/scripts/create-env.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e unset SSH_AUTH_SOCK diff --git a/scripts/delete-remote-branches.sh b/scripts/delete-remote-branches.sh index 414e7f6e7..02fdd4989 100755 --- a/scripts/delete-remote-branches.sh +++ b/scripts/delete-remote-branches.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh # Delete all remote branches that don't have a local copy. remote="rad" diff --git a/scripts/git-hook-template.sh b/scripts/git-hook-template.sh index 8977dff37..ece378580 100644 --- a/scripts/git-hook-template.sh +++ b/scripts/git-hook-template.sh @@ -1,35 +1,35 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -euo pipefail readonly HOOK="${HOOK:-$(basename "$0")}" -if ! [[ "$HOOK" =~ ^(pre-(commit|push)|post-checkout|commit-msg)$ ]] -then - echo "Unknown hook '${HOOK}'." - exit 1 -fi +case "$HOOK" in + pre-commit|pre-push|post-checkout|commit-msg) ;; + *) + echo "Unknown hook '${HOOK}'." + exit 1 + ;; +esac -readonly SENSITIVE_FILES=("justfile" "build.rs" "rust-toolchain.toml") readonly BASE_BRANCH="main" -# Check which files were modified compared to the base branch. -mapfile -t CHANGED_FILES < <(comm -12 \ - <(git diff --name-only "${BASE_BRANCH}" | sort) \ - <(IFS=$'\n'; echo "${SENSITIVE_FILES[*]}" | sort) \ -) +# Check which sensitive files were modified compared to the base branch. +CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH" -- \ + justfile build.rs rust-toolchain.toml) -if [ ${#CHANGED_FILES[@]} -gt 0 ] +if [ -n "$CHANGED_FILES" ] then echo "⚠️ WARNING: Sensitive files have been modified relative to $BASE_BRANCH." echo "Executing this hook may run arbitrary code from the modified files." echo "" - git --no-pager diff "$BASE_BRANCH" -- "${SENSITIVE_FILES[@]}" + git --no-pager diff "$BASE_BRANCH" -- justfile build.rs rust-toolchain.toml # Read from /dev/tty because stdin is not attached to the terminal in Git hooks. exec < /dev/tty - read -r -p "⚠️ Do you want to continue executing the '${HOOK}' hook? [y/N] " response + printf "⚠️ Do you want to continue executing the '%s' hook? [y/N] " "$HOOK" + read -r response case "$response" in [yY][eE][sS]|[yY]) echo "Continuing with '${HOOK}' hook…" diff --git a/scripts/import-issue.sh b/scripts/import-issue.sh index d293e72ea..c870edac6 100755 --- a/scripts/import-issue.sh +++ b/scripts/import-issue.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env sh # # Import a GitHub issue into Radicle. # @@ -16,25 +16,8 @@ if ! command -v rad > /dev/null; then echo "Error: rad is not installed" ; exit 1 fi -if ! command -v pcregrep > /dev/null; then - echo "Error: pcregrep is not installed" ; exit 1 -fi - -if ! command -v sed > /dev/null; then - echo "Error: sed is not installed" ; exit 1 -fi - -function removeImgTags { - local html - html="$(cat)" - local imgTags - imgTags="$(echo "$html" | pcregrep -M ']*>')" - - # shellcheck disable=SC2066 - for imgTag in "$imgTags"; do - html="$(echo "$html" | sed -z "s@$imgTag@@")" - done - echo "$html" +removeImgTags() { + jq -Rrs 'gsub("]*>"; "")' } # Check if the correct number of arguments is provided @@ -53,13 +36,17 @@ url="https://api.github.com/repos/${owner}/${repo}/issues/${issue}" response="$(curl -s "$url")" # Extract the title and body from the JSON response -title="$(echo "$response" | jq -r '.title')" -body="$(echo "$response" | jq -r '.body' | removeImgTags)" -labels="$(echo "$response" | jq -r '.labels | .[].name')" +title="$(printf '%s\n' "$response" | jq -r '.title')" +body="$(printf '%s\n' "$response" | jq -r '.body' | removeImgTags)" +labels="$(printf '%s\n' "$response" | jq -r '.labels | .[].name')" -tags=() -for label in $labels; do - tags+=("--label" "$label") -done +set -- +while IFS= read -r label; do + if [ -n "$label" ]; then + set -- "$@" --label "$label" + fi +done < "$tmp/tracked" +# The child shell expands these variables. +# shellcheck disable=SC2016 +xargs -0 sh -c ' + for file do + if [ -f "$file" ] && [ ! -L "$file" ]; then + printf "%s\000" "$file" + fi + done +' sh < "$tmp/tracked" > "$tmp/files" -if [ ${#files[@]} -eq 0 ]; then +if [ ! -s "$tmp/files" ]; then exit 0 fi # Checksum files before sed so we detect only changes it makes. -before=$(printf '%s\0' "${files[@]}" | xargs -0 md5sum) +xargs -0 md5sum < "$tmp/files" > "$tmp/before" standalone_wildcard='^[[:space:]]*\.\.\.[[:space:]]*$' git_hex_range='[0-9a-f]{4,}\.\.\.[0-9a-f]{4,}' @@ -30,20 +36,20 @@ bracket_wildcard='\[[[:space:]]*\.\.\.[[:space:]]*\]' # Detect GNU vs BSD sed to apply the correct in-place flag if sed --version >/dev/null 2>&1; then - sed_cmd=(sed -i -E) + set -- -i -E else - sed_cmd=(sed -i '' -E) + set -- -i '' -E fi -printf '%s\0' "${files[@]}" | xargs -0 "${sed_cmd[@]}" \ +xargs -0 sed "$@" \ -e "/${standalone_wildcard}/b" \ -e "/${git_hex_range}/b" \ -e "/${git_ref_range}/b" \ - -e "/${bracket_wildcard}/b" \ + -e "/${bracket_wildcard}/b" < "$tmp/files" -after=$(printf '%s\0' "${files[@]}" | xargs -0 md5sum) +xargs -0 md5sum < "$tmp/files" > "$tmp/after" -changed=$(diff <(echo "$before") <(echo "$after") | grep '^>' | sed 's/^> [a-f0-9]* //' || true) +changed=$(diff "$tmp/before" "$tmp/after" | grep '^>' | sed 's/^> [a-f0-9]* //' || true) if [ -n "$changed" ]; then echo "error: Replaced '...' with '…' (U+2026) in the following files:" diff --git a/scripts/just/check-hooks.sh b/scripts/just/check-hooks.sh index ce5f45172..6fd764ca6 100755 --- a/scripts/just/check-hooks.sh +++ b/scripts/just/check-hooks.sh @@ -1,11 +1,11 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -e HOOK_SCRIPT=$1 HOOKS=$2 TEMPLATE="$HOOK_SCRIPT" -OUTDATED=() +OUTDATED="" MISSING=0 TOTAL=0 @@ -13,9 +13,9 @@ for hook in $HOOKS; do TOTAL=$((TOTAL + 1)) if [ ! -f ".git/hooks/$hook" ]; then MISSING=$((MISSING + 1)) - OUTDATED+=("$hook") + OUTDATED="$OUTDATED $hook" elif ! cmp -s "$TEMPLATE" ".git/hooks/$hook"; then - OUTDATED+=("$hook") + OUTDATED="$OUTDATED $hook" fi done @@ -23,12 +23,12 @@ if [ "$MISSING" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then echo "" echo "${HINT}No git hooks are installed. Run 'just install-hooks' to install them.${NORMAL}" echo "" -elif [ ${#OUTDATED[@]} -gt 0 ]; then +elif [ -n "$OUTDATED" ]; then echo "" echo "${WARN}WARNING: The following git hooks are missing or out of date:${NORMAL}" echo "" - for hook in "${OUTDATED[@]}"; do - echo -e "\t$hook" + for hook in $OUTDATED; do + printf '\t%s\n' "$hook" done echo "" echo "${HINT}Check them with 'diff $HOOK_SCRIPT .git/hooks/' then run 'just install-hooks'${NORMAL}" diff --git a/scripts/just/check-keywords.sh b/scripts/just/check-keywords.sh index 828a9e967..c63d92241 100755 --- a/scripts/just/check-keywords.sh +++ b/scripts/just/check-keywords.sh @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -e echo "${CHECK}Checking for forbidden words in staged files…${NORMAL}" diff --git a/scripts/just/commit-msg.sh b/scripts/just/commit-msg.sh index 004e459c5..a987720d1 100755 --- a/scripts/just/commit-msg.sh +++ b/scripts/just/commit-msg.sh @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -euo pipefail FILE=$1 diff --git a/scripts/just/format-nix.sh b/scripts/just/format-nix.sh index f8c6a9a37..46a314969 100755 --- a/scripts/just/format-nix.sh +++ b/scripts/just/format-nix.sh @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh if command -v alejandra >/dev/null 2>&1; then alejandra --check . diff --git a/scripts/just/install-hooks.sh b/scripts/just/install-hooks.sh index 5f7439bad..9e03a4660 100755 --- a/scripts/just/install-hooks.sh +++ b/scripts/just/install-hooks.sh @@ -1,11 +1,12 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -e HOOK_SCRIPT=$1 HOOKS=$2 -read -r -p "Overwrite existing hooks '${HOOKS}'? [y/N] " confirm -[[ "$confirm" == "y" ]] || exit 1 +printf "Overwrite existing hooks '%s'? [y/N] " "$HOOKS" +read -r confirm +[ "$confirm" = "y" ] || exit 1 for hook in $HOOKS; do if [ -f ".git/hooks/$hook" ]; then diff --git a/scripts/just/verify-tool.sh b/scripts/just/verify-tool.sh index ebb584a6c..ca7122aea 100755 --- a/scripts/just/verify-tool.sh +++ b/scripts/just/verify-tool.sh @@ -1,4 +1,4 @@ -#! /usr/bin/env bash +#! /usr/bin/env sh set -e TOOL=$1 PKG_NAME=$2 diff --git a/scripts/patch-rebase.sh b/scripts/patch-rebase.sh index 3f68ee38c..a8d495ae1 100755 --- a/scripts/patch-rebase.sh +++ b/scripts/patch-rebase.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /usr/bin/env sh set -e if [ "$#" -lt 1 ]; then diff --git a/scripts/show-sigrefs.sh b/scripts/show-sigrefs.sh index 8279502c5..13a89e550 100755 --- a/scripts/show-sigrefs.sh +++ b/scripts/show-sigrefs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env sh set -e if [ "$#" -lt 2 ]; then @@ -23,4 +23,9 @@ actual=$(echo "$actual" | sed "s@refs/namespaces/$REMOTE/@@") # Remove `sigrefs` itself. actual=$(echo "$actual" | grep -v "refs/rad/sigrefs$") -diff <(echo "$signed") <(echo "$actual") --color=always -y --minimal -W 240 +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' 0 +trap 'exit 1' HUP INT TERM +printf '%s\n' "$signed" > "$tmp/signed" +printf '%s\n' "$actual" > "$tmp/actual" +diff --color=always -y --minimal -W 240 "$tmp/signed" "$tmp/actual"