diff --git a/tool/check/tooling.sh b/tool/check/tooling.sh index 776d4e3ad..847bed75f 100755 --- a/tool/check/tooling.sh +++ b/tool/check/tooling.sh @@ -75,7 +75,20 @@ done while IFS= read -r script; do bash -n "$script" 2>/dev/null || report "$script: does not parse" - [[ -x $script ]] || report "$script: is not executable (chmod +x)" + case "${OSTYPE:-}" in + msys*|cygwin*) + # Windows filesystems do not expose Unix executable bits reliably. + # Git's index is authoritative for the mode that macOS, Linux, and CI + # receive after checkout. + mode="$(git ls-files --stage -- "$script" | awk 'NR == 1 { print $1 }')" + [[ $mode == 100755 ]] || + report "$script: is not executable in git (git update-index --chmod=+x)" + ;; + *) + [[ -x $script ]] || + report "$script: is not executable (chmod +x)" + ;; + esac head -1 "$script" | grep -q '^#!/usr/bin/env bash$' || [[ $script == tool/dev/_lib.sh ]] || diff --git a/tool/dev/_lib.sh b/tool/dev/_lib.sh index 7534a20ea..b1624597a 100755 --- a/tool/dev/_lib.sh +++ b/tool/dev/_lib.sh @@ -80,9 +80,48 @@ EOF # is identical either way. local owned owned="$(cd "$root" && mise where flutter 2>/dev/null || true)" - if [[ -z $owned || $resolved != "$owned"/* ]]; then + if [[ -z $owned ]]; then printf '\n flutter resolves to %s\n' "$resolved" >&2 - printf ' which is not inside mise (%s).\n\n' "${owned:-not installed}" >&2 + printf ' but mise reports that flutter is not installed.\n\n' >&2 + printf ' Run `mise install` from %s.\n\n' "$root" >&2 + exit 1 + fi + + local resolved_for_compare="$resolved" + local owned_for_compare="$owned" + + case "${OSTYPE:-}" in + msys*|cygwin*) + # Git Bash/MSYS and Cygwin may express one Windows path as C:\Users\... + # in one command and /c/Users/... in another. Normalize both to the same + # mixed Windows form before checking ownership; do not skip the check. + if ! command -v cygpath >/dev/null 2>&1; then + cat >&2 <<'EOF' + + cygpath is required to verify that flutter belongs to mise, but it could not + be found in this MSYS/Cygwin environment. + +EOF + exit 1 + fi + + resolved_for_compare="$(cygpath -am -- "$resolved")" + owned_for_compare="$(cygpath -am -- "$owned")" + + # Windows paths are case-insensitive. Mixed form uses forward slashes, + # and removing a trailing slash keeps the prefix test unambiguous. + resolved_for_compare="${resolved_for_compare,,}" + owned_for_compare="${owned_for_compare%/}" + owned_for_compare="${owned_for_compare,,}" + ;; + *) + owned_for_compare="${owned_for_compare%/}" + ;; + esac + + if [[ $resolved_for_compare != "$owned_for_compare"/* ]]; then + printf '\n flutter resolves to %s\n' "$resolved" >&2 + printf ' which is not inside mise (%s).\n\n' "$owned" >&2 printf ' Run `mise install` from %s.\n\n' "$root" >&2 exit 1 fi