From 861dbd36ddf4506e5991843722dd2de1cd96597e Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 21 Sep 2026 21:16:09 -0500 Subject: [PATCH 1/3] fix: honor Git effective hook path --- .ores-lint/install-git-hooks.sh | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/.ores-lint/install-git-hooks.sh b/.ores-lint/install-git-hooks.sh index c4dbce2..ed24045 100755 --- a/.ores-lint/install-git-hooks.sh +++ b/.ores-lint/install-git-hooks.sh @@ -1,19 +1,31 @@ #!/bin/sh # Optional: install a pre-push hook that runs ores-lint. -# Not installed automatically by the rollout - run this yourself per repo. -set -u -ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "not a git repo"; exit 1; } -HOOK="$ROOT/.git/hooks/pre-push" -if [ -e "$HOOK" ] && ! grep -q 'ores-lint' "$HOOK"; then - echo "refusing to clobber an existing pre-push hook: $HOOK" +set -eu +ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "not a git repo" >&2; exit 1; } +HOOKS_PATH=$(git -C "$ROOT" config --get core.hooksPath 2>/dev/null || true) +case "$HOOKS_PATH" in + "") GIT_DIR=$(git -C "$ROOT" rev-parse --absolute-git-dir); HOOK_DIR="$GIT_DIR/hooks" ;; + /*) HOOK_DIR="$HOOKS_PATH" ;; + '~/'*) HOOK_DIR="$HOME/${HOOKS_PATH#??}" ;; + *) HOOK_DIR="$ROOT/$HOOKS_PATH" ;; +esac +mkdir -p "$HOOK_DIR" +HOOK="$HOOK_DIR/pre-push" +if [ -e "$HOOK" ] && ! grep -Fq 'installed by .ores-lint/install-git-hooks.sh' "$HOOK"; then + echo "refusing to clobber an existing pre-push hook: $HOOK" >&2 exit 1 fi cat > "$HOOK" <<'INNER' #!/bin/sh # installed by .ores-lint/install-git-hooks.sh -[ -x "$(git rev-parse --show-toplevel)/.ores-lint/lint.sh" ] && \ - sh "$(git rev-parse --show-toplevel)/.ores-lint/lint.sh" -exit 0 +set -eu +ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 1 +LINT="$ROOT/.ores-lint/lint.sh" +if [ ! -x "$LINT" ]; then + echo "ores-lint pre-push: missing executable $LINT" >&2 + exit 1 +fi +exec sh "$LINT" INNER chmod +x "$HOOK" echo "installed $HOOK" From df7e2fd1b43ec124b09bdf671a1c9a00ff51aa4f Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 21 Sep 2026 21:16:21 -0500 Subject: [PATCH 2/3] test: cover effective Git hook paths --- .ores-lint/test-install-git-hooks.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .ores-lint/test-install-git-hooks.sh diff --git a/.ores-lint/test-install-git-hooks.sh b/.ores-lint/test-install-git-hooks.sh new file mode 100644 index 0000000..9e8bbf9 --- /dev/null +++ b/.ores-lint/test-install-git-hooks.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -eu +SOURCE_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +TMP_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/ores-lint-hooks.XXXXXX") +trap 'rm -rf "$TMP_ROOT"' EXIT HUP INT TERM +new_repo() { + repo="$TMP_ROOT/$1" + mkdir -p "$repo/.ores-lint" + git -C "$repo" init -q + cp "$SOURCE_DIR/install-git-hooks.sh" "$repo/.ores-lint/install-git-hooks.sh" + printf '%s\n' '#!/bin/sh' ': > "$(git rev-parse --show-toplevel)/lint-ran"' > "$repo/.ores-lint/lint.sh" + chmod +x "$repo/.ores-lint/lint.sh" + printf '%s\n' "$repo" +} +repo=$(new_repo default) +(cd "$repo" && sh .ores-lint/install-git-hooks.sh && hook="$(git rev-parse --absolute-git-dir)/hooks/pre-push" && test -x "$hook" && "$hook" && test -f lint-ran) +repo=$(new_repo relative) +(cd "$repo" && git config core.hooksPath .githooks && sh .ores-lint/install-git-hooks.sh && test -x .githooks/pre-push && .githooks/pre-push && test -f lint-ran && test ! -e "$(git rev-parse --absolute-git-dir)/hooks/pre-push") +repo=$(new_repo absolute) +absolute_hooks="$TMP_ROOT/absolute-hooks" +(cd "$repo" && git config core.hooksPath "$absolute_hooks" && sh .ores-lint/install-git-hooks.sh && test -x "$absolute_hooks/pre-push") +repo=$(new_repo tilde) +mkdir -p "$TMP_ROOT/home" +(cd "$repo" && HOME="$TMP_ROOT/home" git config core.hooksPath '~/custom-hooks' && HOME="$TMP_ROOT/home" sh .ores-lint/install-git-hooks.sh && test -x "$TMP_ROOT/home/custom-hooks/pre-push") +repo=$(new_repo conflict) +(cd "$repo" && git config core.hooksPath .githooks && mkdir -p .githooks && printf '%s\n' '#!/bin/sh' 'echo existing' > .githooks/pre-push && chmod +x .githooks/pre-push && before=$(cat .githooks/pre-push) && if sh .ores-lint/install-git-hooks.sh; then exit 1; fi && test "$(cat .githooks/pre-push)" = "$before") +printf '%s\n' 'ores-lint Git hook installer contract: PASS' From 86dc5a220a7e1d2d4590fb20f59a535f3a02b24d Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 21 Sep 2026 21:16:32 -0500 Subject: [PATCH 3/3] ci: verify ores-lint hook installer contract --- .github/workflows/ores-lint-hook-contract.yml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/ores-lint-hook-contract.yml diff --git a/.github/workflows/ores-lint-hook-contract.yml b/.github/workflows/ores-lint-hook-contract.yml new file mode 100644 index 0000000..b00980b --- /dev/null +++ b/.github/workflows/ores-lint-hook-contract.yml @@ -0,0 +1,37 @@ +name: ores-lint hook contract + +on: + pull_request: + paths: + - '.ores-lint/install-git-hooks.sh' + - '.ores-lint/test-install-git-hooks.sh' + - '.github/workflows/ores-lint-hook-contract.yml' + push: + branches: [master] + paths: + - '.ores-lint/install-git-hooks.sh' + - '.ores-lint/test-install-git-hooks.sh' + - '.github/workflows/ores-lint-hook-contract.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ores-lint-hook-contract-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + contract: + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Syntax + run: | + sh -n .ores-lint/install-git-hooks.sh + sh -n .ores-lint/test-install-git-hooks.sh + - name: Installer contract + run: sh .ores-lint/test-install-git-hooks.sh