feat(pre-push-safety-net): preserve existing hooks during explicit integration

This commit is contained in:
Marius Ciepluch
2026-09-13 17:51:28 +02:00
parent f04955c367
commit 749c7ab45d
9 changed files with 294 additions and 14 deletions
+62
View File
@@ -282,3 +282,65 @@ is separate from alert eligibility: current admission is always applied. No hook
by replay. Artifact timings record discovery, graph and query hits; identical-tip hits do
not establish representative changed-code latency. Corrupt, partial or incompatible entries
are misses, with remaining work constrained by the same push deadline.
### Experimental pre-push integration
The default capability registry is empty pending independent qualification. Installing
this interface does not make the project rollout-ready. Advisory is the default;
blocking and strict incomplete-coverage handling are separate explicit choices.
Put the installed `ousast` executable on the Git process's PATH. From this tool's
checkout, explicitly install into the desired repository:
```sh
ops/install-pre-push /absolute/path/to/repository
```
The installer resolves Git's effective hook directory (including `core.hooksPath`),
leaves that configuration untouched, and refuses to overwrite any existing hook.
For an existing hook, inspect it and explicitly move it aside **before** installation:
```sh
hook_dir=$(git -C /absolute/path/to/repository rev-parse --path-format=absolute --git-path hooks)
hook="$hook_dir/pre-push"
test ! -e "$hook.before-ousast" && test ! -L "$hook.before-ousast" && mv -- "$hook" "$hook.before-ousast"
ops/install-pre-push /absolute/path/to/repository
```
The wrapper chains that sibling hook with the exact remote arguments and original
stdin bytes, even when analysis blocks. A prior rejection always remains a rejection.
Only an explicitly chained hook executes project-owned hook code; analysis executes
no project code. The existing hook retains its own runtime, outside the safety-net
analysis deadline. Integration input retention is bounded to 1 MiB and the analysis
deadline; failure to retain complete input stops the integration rather than feeding
partial input to another consumer.
Artifacts default to `$XDG_CACHE_HOME/openultrasast/push` (or `~/.cache/openultrasast/push`).
Set `OUSAST_ARTIFACT_DIR` to an absolute directory outside the repository if needed.
`OUSAST_PUSH_DEADLINE` defaults to 30 seconds. `OUSAST_PUSH_MODE=blocking` opts into
blocking; `OUSAST_INCOMPLETE_COVERAGE=block` additionally blocks incomplete coverage
in that mode. These variables do not enable detector capabilities or model calls.
For a new branch, add an explicit `--comparison-base <local-ref>` to both command
branches of the installed wrapper if a suitable local base exists. Without it, a new
branch records missing-base coverage rather than silently selecting HEAD or fetching.
Direct stdin integration is `ousast pre-push . --artifact /outside/push.json
--remote "$1" "$2"`; explicit `--base` and `--head` replay remains available.
To remove, inspect the installed wrapper first. If it is unchanged, this guarded
recipe removes only the supplied wrapper and restores the previous hook:
```sh
hook_dir=$(git -C /absolute/path/to/repository rev-parse --path-format=absolute --git-path hooks)
hook="$hook_dir/pre-push"
if cmp -s ops/pre-push "$hook"; then
rm -- "$hook"
if test -e "$hook.before-ousast" || test -L "$hook.before-ousast"; then
mv -n -- "$hook.before-ousast" "$hook"
fi
else
printf '%s\n' 'Wrapper differs: inspect and remove your integration changes manually.'
fi
```
Neither installation nor removal modifies source, the index, refs, or hook-path
configuration. Keep any prior hook backup until restoration is confirmed.
+25
View File
@@ -0,0 +1,25 @@
#!/bin/sh
# Explicit installation only. Never edit core.hooksPath or overwrite a hook.
set -eu
repo=${1:-.}
source_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
hook_dir=$(git -C "$repo" rev-parse --path-format=absolute --git-path hooks)
hook="$hook_dir/pre-push"
mkdir -p -- "$(dirname -- "$hook")"
# Resolve only the directory: Git canonicalizes a final hook symlink to its target.
# O_EXCL refuses every pre-existing final path, including dangling symlinks.
python3 - "$source_dir/pre-push" "$hook" <<'PY'
import os
import shutil
import sys
try:
with open(sys.argv[1], 'rb') as source:
fd = os.open(sys.argv[2], os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o755)
with os.fdopen(fd, 'wb') as target:
shutil.copyfileobj(source, target)
os.fchmod(target.fileno(), 0o755)
except OSError:
print('Installation refused: hook exists or cannot be created. See ops/README.md.', file=sys.stderr)
sys.exit(1)
PY
printf 'Installed %s\n' "$hook"
Executable
+17
View File
@@ -0,0 +1,17 @@
#!/bin/sh
# Explicitly copy using the non-overwriting recipe in ops/README.md.
# A prior hook is chained only after you explicitly move it to this sibling path.
artifact_dir=${OUSAST_ARTIFACT_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/openultrasast/push}
prior="$0.before-ousast"
if [ -e "$prior" ] || [ -L "$prior" ]; then
exec ousast pre-push . --artifact "$artifact_dir/push-$$.json" \
--deadline "${OUSAST_PUSH_DEADLINE:-30}" \
--mode "${OUSAST_PUSH_MODE:-advisory}" \
--incomplete-coverage "${OUSAST_INCOMPLETE_COVERAGE:-allow}" \
--prior-hook "$prior" --remote "$1" "$2"
fi
exec ousast pre-push . --artifact "$artifact_dir/push-$$.json" \
--deadline "${OUSAST_PUSH_DEADLINE:-30}" \
--mode "${OUSAST_PUSH_MODE:-advisory}" \
--incomplete-coverage "${OUSAST_INCOMPLETE_COVERAGE:-allow}" \
--remote "$1" "$2"