- Rename post-commit-ots → post-commit - Rename pre-commit-ots → pre-commit - Remove legacy post-commit and pre-commit symlinks - Update install.sh and documentation - Simplified: only 2 hook files with standard names Hooks are now named exactly as git expects them, making manual installation more intuitive.
125 lines
3.3 KiB
Bash
Executable file
125 lines
3.3 KiB
Bash
Executable file
#!/bin/bash
|
|
# Git OpenTimestamp Pre-Commit Backfill Hook (ots CLI version)
|
|
# Requires: opentimestamps-client (pipx install opentimestamps-client)
|
|
|
|
set -e
|
|
|
|
OUTPUT_DIR=".ots"
|
|
STATUS_CACHE="$OUTPUT_DIR/.attestation-cache"
|
|
|
|
mkdir -p "$OUTPUT_DIR"
|
|
|
|
# Initialize cache
|
|
if [ ! -f "$STATUS_CACHE" ]; then
|
|
echo "# Format: commit-hash:status:timestamp" > "$STATUS_CACHE"
|
|
fi
|
|
|
|
get_cached_status() {
|
|
local commit="$1"
|
|
local cache_line=$(grep "^$commit:" "$STATUS_CACHE" 2>/dev/null | tail -1)
|
|
if [ -n "$cache_line" ]; then
|
|
local status=$(echo "$cache_line" | cut -d: -f2)
|
|
local timestamp=$(echo "$cache_line" | cut -d: -f3)
|
|
local now=$(date +%s)
|
|
if [ "$((now - timestamp))" -lt 3600 ]; then
|
|
echo "$status"
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
cache_status() {
|
|
local commit="$1"
|
|
local status="$2"
|
|
echo "$commit:$status:$(date +%s)" >> "$STATUS_CACHE"
|
|
}
|
|
|
|
generate_proof() {
|
|
local hash="$1"
|
|
local output="$2"
|
|
local temp_file=$(mktemp)
|
|
local temp_ots="${temp_file}.ots"
|
|
|
|
python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('$hash'))" > "$temp_file"
|
|
ots stamp "$temp_file" 2>/dev/null
|
|
|
|
if [ -f "$temp_ots" ]; then
|
|
mv "$temp_ots" "$output"
|
|
rm -f "$temp_file"
|
|
return 0
|
|
fi
|
|
rm -f "$temp_file"
|
|
return 1
|
|
}
|
|
|
|
is_attested() {
|
|
local proof_file="$1"
|
|
local pending_count=$(ots info "$proof_file" 2>&1 | grep -c "PendingAttestation" || echo "0")
|
|
[ "$pending_count" -eq 0 ]
|
|
}
|
|
|
|
upgrade_proof() {
|
|
local proof_file="$1"
|
|
ots upgrade "$proof_file" 2>/dev/null
|
|
}
|
|
|
|
echo "[ots] Backfilling proofs..."
|
|
|
|
COMMITS=$(git rev-list --reverse HEAD)
|
|
TOTAL=$(echo "$COMMITS" | wc -l)
|
|
CURRENT=0
|
|
UPDATED=0
|
|
|
|
for COMMIT in $COMMITS; do
|
|
CURRENT=$((CURRENT + 1))
|
|
PROOF_FILE="$OUTPUT_DIR/${COMMIT}.ots"
|
|
|
|
if [ $CURRENT -le 3 ] || [ $CURRENT -eq $TOTAL ]; then
|
|
echo "[ots] Processing $CURRENT/$TOTAL: ${COMMIT:0:8}"
|
|
elif [ $CURRENT -eq 4 ]; then
|
|
echo "[ots] ... processing remaining ..."
|
|
fi
|
|
|
|
if [ -f "$PROOF_FILE" ]; then
|
|
CACHED_STATUS=$(get_cached_status "$COMMIT" || echo "")
|
|
|
|
[ "$CACHED_STATUS" = "attested" ] && continue
|
|
|
|
if is_attested "$PROOF_FILE"; then
|
|
cache_status "$COMMIT" "attested"
|
|
continue
|
|
fi
|
|
|
|
cache_status "$COMMIT" "pending"
|
|
|
|
CACHE_LINE=$(grep "^$COMMIT:" "$STATUS_CACHE" | tail -1)
|
|
CACHE_TIME=$(echo "$CACHE_LINE" | cut -d: -f3)
|
|
CACHE_AGE=$(($(date +%s) - CACHE_TIME))
|
|
|
|
[ "$CACHE_AGE" -lt 600 ] && continue
|
|
|
|
if upgrade_proof "$PROOF_FILE"; then
|
|
cache_status "$COMMIT" "attested"
|
|
UPDATED=$((UPDATED + 1))
|
|
fi
|
|
else
|
|
if generate_proof "$COMMIT" "$PROOF_FILE"; then
|
|
cache_status "$COMMIT" "pending"
|
|
UPDATED=$((UPDATED + 1))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Update latest proof
|
|
LATEST_COMMIT=$(git rev-parse HEAD)
|
|
[ -f "$OUTPUT_DIR/${LATEST_COMMIT}.ots" ] && cp "$OUTPUT_DIR/${LATEST_COMMIT}.ots" "$OUTPUT_DIR/proof.ots"
|
|
|
|
# Save commit chain
|
|
rm -f "$OUTPUT_DIR/commit-chain.txt"
|
|
git rev-list HEAD | while read COMMIT; do
|
|
PREV=$(git rev-parse ${COMMIT}^1 2>/dev/null || echo "")
|
|
[ -n "$PREV" ] && echo "$COMMIT:$PREV" >> "$OUTPUT_DIR/commit-chain.txt"
|
|
done
|
|
|
|
echo "[ots] Backfill complete: $UPDATED updated"
|