mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
Merge PR #824 into main
fix(dev): preserve pre-push hooks across worktree installs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm # Conflicts: # CHANGELOG.md
This commit is contained in:
@@ -17,6 +17,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
release's `hooks/` bundle beside the client, so auto-wire no longer fails
|
||||
with "could not locate hooks directory" for script-based harnesses on a host
|
||||
where `install-hooks` never ran. (#874)
|
||||
- Fixed pre-push installation from linked worktrees and preserved the managed
|
||||
block's position during reinstallation. Configured `core.hooksPath` overrides
|
||||
and ambiguous markers are rejected without replacing the existing hook. The
|
||||
block now keeps its shell options and `SSL_CERT_FILE` inside its subshell and
|
||||
propagates a failure explicitly, so user hook commands after it keep their
|
||||
own semantics and a failing test run still blocks the push. (#824)
|
||||
- Isolated the pre-push test process from Git's repository environment and
|
||||
global/system configuration so fixture commands use their own repositories.
|
||||
Existing installations need to run `scripts/install-git-hooks.sh` again. (#824)
|
||||
- Auto-improve review no longer stages a proposal whose LLM-produced page
|
||||
path contains a Windows-illegal character (e.g. a `:` copied from a
|
||||
conventional-commit subject). That path passed the deliberately tolerant
|
||||
|
||||
+15
-3
@@ -77,9 +77,21 @@ Skipped tests still count as "skipped" in the summary, never hidden, and two
|
||||
independent things run them anyway: the pre-push hook and CI.
|
||||
|
||||
Install the hook once per clone with `scripts/install-git-hooks.sh` (from Git
|
||||
Bash on Windows). It appends or updates only ai-memory's managed block in
|
||||
`.git/hooks/pre-push`, preserving any existing hook body. Bypass it on a
|
||||
work-in-progress branch with `git push --no-verify`.
|
||||
Bash on Windows). It can run from the main checkout or a linked worktree;
|
||||
both use the shared repository hook directory. Reinstallation replaces
|
||||
ai-memory's managed block in place, preserving surrounding user commands and
|
||||
their order. If `core.hooksPath` is set, the installer stops before writing;
|
||||
integrate the block through your existing hook manager instead. Incomplete or
|
||||
duplicate managed markers also stop installation and leave the hook unchanged.
|
||||
Bypass it on a work-in-progress branch with `git push --no-verify`.
|
||||
|
||||
The managed test block clears Git's repository environment and disables global
|
||||
and system Git configuration for Cargo and its children. Fixture commands can
|
||||
then use their own repositories without inheriting the checkout being pushed.
|
||||
The publishing Git process and other hook code retain their configuration, and
|
||||
the block's shell options stay inside it; a failing test run still fails the
|
||||
hook even when your own commands follow the block without `set -e`.
|
||||
Run the installer again to update an existing installation.
|
||||
|
||||
Integration tests live in `tests/suite/` per crate and compile into the
|
||||
crate's own test harness (declare a new file with `mod name;` in
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+108
-35
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs this repo's pre-push hook into .git/hooks without discarding an
|
||||
# existing user hook. Run once per clone (from Git Bash on Windows):
|
||||
# Installs this repo's pre-push hook without moving existing user commands.
|
||||
# Run once per clone (from Git Bash on Windows):
|
||||
#
|
||||
# scripts/install-git-hooks.sh
|
||||
#
|
||||
@@ -12,48 +12,121 @@
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(git rev-parse --show-toplevel)
|
||||
hook="$repo_root/.git/hooks/pre-push"
|
||||
cd "$repo_root"
|
||||
if git config --get core.hooksPath >/dev/null; then
|
||||
echo 'core.hooksPath is set; this installer only manages the shared repository hook directory' >&2
|
||||
exit 1
|
||||
else
|
||||
# `$?` is still the condition's status here: 1 means the key is unset, and
|
||||
# anything else is a Git failure the installer must not guess past.
|
||||
config_status=$?
|
||||
if [[ "$config_status" -ne 1 ]]; then
|
||||
exit "$config_status"
|
||||
fi
|
||||
fi
|
||||
hook=$(git rev-parse --git-path hooks/pre-push)
|
||||
begin="# >>> ai-memory pre-push >>>"
|
||||
end="# <<< ai-memory pre-push <<<"
|
||||
tmp=$(mktemp "${hook}.XXXXXX")
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
|
||||
if [[ -f "$hook" ]]; then
|
||||
awk -v begin="$begin" -v end="$end" '
|
||||
$0 == begin { skip = 1; next }
|
||||
$0 == end { skip = 0; next }
|
||||
!skip { print }
|
||||
' "$hook" > "$tmp"
|
||||
if grep -q '[^[:space:]]' "$tmp"; then
|
||||
printf '\n' >> "$tmp"
|
||||
managed_block=$(cat <<'HOOK'
|
||||
# >>> ai-memory pre-push >>>
|
||||
# Runs the full test tier before a push. See scripts/install-git-hooks.sh.
|
||||
|
||||
# Git's hook environment would redirect fixture commands into this checkout.
|
||||
# Isolate Cargo and its children, and keep this block's shell options and
|
||||
# exports away from user hook code around it.
|
||||
(
|
||||
set -euo pipefail
|
||||
|
||||
# macOS: stop reqwest re-reading the Keychain in every test process.
|
||||
if [ "$(uname -s 2>/dev/null || true)" = "Darwin" ] && [ -z "${SSL_CERT_FILE:-}" ] && [ -f /etc/ssl/cert.pem ]; then
|
||||
export SSL_CERT_FILE=/etc/ssl/cert.pem
|
||||
fi
|
||||
|
||||
# A plain assignment, so `set -e` still aborts if `git rev-parse` fails.
|
||||
git_local_env_vars=$(git rev-parse --local-env-vars)
|
||||
# A surrounding user hook may have narrowed IFS; the list below is split on
|
||||
# newlines, so fall back to the default before splitting it. Unset rather
|
||||
# than assigned: Bash 3.2 expands ANSI-C quoting inside this heredoc.
|
||||
unset IFS
|
||||
# Unquoted on purpose: the output is one variable name per line.
|
||||
for git_local_env_var in $git_local_env_vars; do
|
||||
unset "$git_local_env_var"
|
||||
done
|
||||
# Fixture repositories must not inherit machine settings such as signing.
|
||||
# Git for Windows maps /dev/null to nul.
|
||||
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null
|
||||
|
||||
if command -v cargo-nextest >/dev/null 2>&1; then
|
||||
echo "pre-push: cargo nextest run --workspace -P full"
|
||||
cargo nextest run --workspace -P full
|
||||
else
|
||||
printf '%s\n\n' '#!/usr/bin/env bash' '# Installed by scripts/install-git-hooks.sh.' > "$tmp"
|
||||
echo "pre-push: cargo test --workspace --all-targets (nextest not installed)"
|
||||
cargo test --workspace --all-targets
|
||||
fi
|
||||
)
|
||||
# Bash ignores `set -e` inside a subshell tested by `||`, `&&`, `!` or `if`, so
|
||||
# the subshell stays a plain command and its status is propagated here. A user
|
||||
# hook without `set -e` would otherwise run on and let the push through.
|
||||
ai_memory_pre_push_status=$?
|
||||
if [ "$ai_memory_pre_push_status" -ne 0 ]; then
|
||||
exit "$ai_memory_pre_push_status"
|
||||
fi
|
||||
unset ai_memory_pre_push_status
|
||||
# <<< ai-memory pre-push <<<
|
||||
HOOK
|
||||
)
|
||||
|
||||
has_content=0
|
||||
if [[ -f "$hook" ]]; then
|
||||
if grep -q '[^[:space:]]' "$hook"; then
|
||||
has_content=1
|
||||
else
|
||||
# As above, `$?` is grep's status: 1 is a blank hook, 2 a read error.
|
||||
read_status=$?
|
||||
if [[ "$read_status" -ne 1 ]]; then
|
||||
exit "$read_status"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "${hook%/*}"
|
||||
tmp=$(mktemp "${hook}.XXXXXX")
|
||||
if [[ "$has_content" -eq 1 ]]; then
|
||||
# ENVIRON preserves backslashes; BINMODE prevents Windows CRLF translation.
|
||||
if ! AI_MEMORY_PRE_PUSH_BLOCK="$managed_block" awk -v BINMODE=3 -v begin="$begin" -v end="$end" '
|
||||
{
|
||||
marker = $0
|
||||
sub(/\r$/, "", marker)
|
||||
if (marker == begin) {
|
||||
if (inside || seen) { invalid = 1; exit 1 }
|
||||
inside = seen = 1
|
||||
print ENVIRON["AI_MEMORY_PRE_PUSH_BLOCK"]
|
||||
next
|
||||
}
|
||||
if (marker == end) {
|
||||
if (!inside) { invalid = 1; exit 1 }
|
||||
inside = 0
|
||||
next
|
||||
}
|
||||
if (!inside) print
|
||||
}
|
||||
END {
|
||||
if (invalid || inside) exit 1
|
||||
if (!seen) {
|
||||
print ""
|
||||
print ENVIRON["AI_MEMORY_PRE_PUSH_BLOCK"]
|
||||
}
|
||||
}
|
||||
' "$hook" > "$tmp"; then
|
||||
printf 'invalid managed markers in %s; original unchanged, temporary file retained at %s\n' "$hook" "$tmp" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
printf '%s\n\n' '#!/usr/bin/env bash' '# Installed by scripts/install-git-hooks.sh.' > "$tmp"
|
||||
printf '%s\n' "$managed_block" >> "$tmp"
|
||||
fi
|
||||
|
||||
cat >> "$tmp" <<'HOOK'
|
||||
# >>> ai-memory pre-push >>>
|
||||
# Runs the full test tier before a push. See scripts/install-git-hooks.sh.
|
||||
set -euo pipefail
|
||||
|
||||
# macOS: stop reqwest re-reading the Keychain in every test process.
|
||||
if [ "$(uname -s 2>/dev/null || true)" = "Darwin" ] && [ -z "${SSL_CERT_FILE:-}" ] && [ -f /etc/ssl/cert.pem ]; then
|
||||
export SSL_CERT_FILE=/etc/ssl/cert.pem
|
||||
fi
|
||||
|
||||
if command -v cargo-nextest >/dev/null 2>&1; then
|
||||
echo "pre-push: cargo nextest run --workspace -P full"
|
||||
cargo nextest run --workspace -P full
|
||||
else
|
||||
echo "pre-push: cargo test --workspace --all-targets (nextest not installed)"
|
||||
cargo test --workspace --all-targets
|
||||
fi
|
||||
# <<< ai-memory pre-push <<<
|
||||
HOOK
|
||||
|
||||
mv "$tmp" "$hook"
|
||||
trap - EXIT
|
||||
chmod +x "$hook"
|
||||
echo "installed $hook"
|
||||
|
||||
Reference in New Issue
Block a user