From 8ff7aac0ca60c2758f89951dad51422a95899934 Mon Sep 17 00:00:00 2001 From: Mos Date: Fri, 7 Aug 2026 04:26:36 +0000 Subject: [PATCH] fix(tools/git): detect-platform died silently outside a repo, taking every wrapper with it (#1089) --- .../framework/tools/git/detect-platform.sh | 10 +++- .../git/test-detect-platform-outside-repo.sh | 58 +++++++++++++++++++ packages/mosaic/package.json | 2 +- 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100755 packages/mosaic/framework/tools/git/test-detect-platform-outside-repo.sh diff --git a/packages/mosaic/framework/tools/git/detect-platform.sh b/packages/mosaic/framework/tools/git/detect-platform.sh index 97c6e5b3..a06452d1 100755 --- a/packages/mosaic/framework/tools/git/detect-platform.sh +++ b/packages/mosaic/framework/tools/git/detect-platform.sh @@ -5,7 +5,10 @@ detect_platform() { local remote_url - remote_url=$(git remote get-url origin 2>/dev/null) + # `|| true` is load-bearing under `set -e`: outside a git repo this returns 128 and + # kills the CALLER before the -z check below can run, so the error message that is + # already written here was unreachable. Same idiom as get_gitea_repo_args() below. + remote_url=$(git remote get-url origin 2>/dev/null) || true if [[ -z "$remote_url" ]]; then echo "error: not a git repository or no origin remote" >&2 @@ -39,7 +42,10 @@ detect_platform() { get_repo_info() { local remote_url - remote_url=$(git remote get-url origin 2>/dev/null) + # `|| true` is load-bearing under `set -e`: outside a git repo this returns 128 and + # kills the CALLER before the -z check below can run, so the error message that is + # already written here was unreachable. Same idiom as get_gitea_repo_args() below. + remote_url=$(git remote get-url origin 2>/dev/null) || true if [[ -z "$remote_url" ]]; then echo "error: not a git repository or no origin remote" >&2 diff --git a/packages/mosaic/framework/tools/git/test-detect-platform-outside-repo.sh b/packages/mosaic/framework/tools/git/test-detect-platform-outside-repo.sh new file mode 100755 index 00000000..13955f0e --- /dev/null +++ b/packages/mosaic/framework/tools/git/test-detect-platform-outside-repo.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Regression: detect_platform / get_repo_info must FAIL LOUDLY outside a git repo, +# not kill the caller silently. +# +# Both functions already contained the right error path: +# if [[ -z "$remote_url" ]]; then echo "error: not a git repository..." >&2; return 1; fi +# but under `set -e` -- which every wrapper in this directory uses -- the preceding +# assignment `remote_url=$(git remote get-url origin 2>/dev/null)` returns git's 128 +# outside a repo and terminates the CALLER first. The message was unreachable. +# +# Observed cost: pr-review.sh invoked from a non-repo cwd exits 128 with NO stdout and +# NO stderr, even when -r/--repo and -H/--host are supplied -- the flags documented as +# "skips git-remote inference". Two reviewer seats hit this and correctly reported +# `blocked` with no diagnostic to report. +# +# The control that matters is the LOUD one: asserting "rc != 0" passes on the broken +# build too, because 128 is also non-zero. The test must assert the MESSAGE. +set -uo pipefail +fail=0 +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT + +run_outside() { # $1=function name -> "rc:sawmessage" + local fn="$1" out rc + out=$( cd "$TMP" && bash -c "set -e; source '$HERE/detect-platform.sh'; $fn" 2>&1 ); rc=$? + printf '%s:%s' "$rc" "$(grep -qi 'not a git repository' <<<"$out" && echo yes || echo no)" +} +check() { if [ "$2" = "$3" ]; then echo " PASS $1 ($2)"; else echo " FAIL $1: got $2, want $3"; fail=1; fi; } + +# $TMP must not be inside a git repo. Do not SKIP on failure: be-coder-07 showed the +# original SKIP exited 0, so pointing TMPDIR beneath a git worktree made this test PASS +# against unchanged main. A skip that exits 0 is indistinguishable from a pass. +# GIT_CEILING_DIRECTORIES stops git walking above $TMP, making the condition hold +# regardless of where TMPDIR lives, rather than merely detecting when it does not. +# GIT_CEILING_DIRECTORIES is matched against the PHYSICAL path -- a symlinked TMPDIR +# (/tmp is commonly one) makes the logical path never match, and the ceiling silently +# does nothing. Resolve it before exporting. +TMP="$(cd "$TMP" && pwd -P)" +export GIT_CEILING_DIRECTORIES="$TMP" +if ( cd "$TMP" && git rev-parse --git-dir >/dev/null 2>&1 ); then + echo " FAIL scratch dir is inside a git repo even with GIT_CEILING_DIRECTORIES set;" + echo " the outside-a-repo precondition cannot be established -- refusing to report a result" + exit 1 +fi + +echo "== outside a git repo: rc=1 AND the diagnostic is emitted ==" +check "detect_platform" "$(run_outside detect_platform)" "1:yes" +check "get_repo_info" "$(run_outside get_repo_info)" "1:yes" + +echo "== inside a git repo the functions still work ==" +git init -q "$TMP/repo" 2>/dev/null +git -C "$TMP/repo" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git 2>/dev/null +out=$( cd "$TMP/repo" && bash -c "set -e; source '$HERE/detect-platform.sh'; detect_platform" 2>&1 ); rc=$? +if [ "$rc" -eq 0 ] && grep -qi 'gitea' <<<"$out"; then echo " PASS detect_platform in-repo (rc=0, $out)" +else echo " FAIL detect_platform in-repo: rc=$rc out=$out"; fail=1; fi + +[ "$fail" -eq 0 ] && echo "OK detect-platform fails loudly outside a repo" || echo "FAILED" +exit "$fail" diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 23234ef3..7096794d 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -25,7 +25,7 @@ "lint": "eslint src", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests && pnpm run test:framework-shell", - "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" + "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" }, "dependencies": { "@mosaicstack/brain": "workspace:*",