blob: dc328195c3fd796950ae29dc818928dd28a6101b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#!/usr/bin/env bash
# ==============================================================================
# post-receive hook — Mirror push to multiple forges
# ==============================================================================
#
# Place this file in your bare repository:
# /path/to/project-tick.git/hooks/post-receive
#
# Make it executable:
# chmod +x hooks/post-receive
#
# Configuration:
# Set mirror remotes in the bare repo:
#
# git remote add github git@github.com:Project-Tick/Project-Tick.git
# git remote add gitlab git@gitlab.com:Project-Tick/Project-Tick.git
# git remote add codeberg git@codeberg.org:Project-Tick/Project-Tick.git
# git remote add sourceforge ssh://USERNAME@git.code.sf.net/p/project-tick/code
#
# Or use HTTPS with token auth:
#
# git remote add github https://x-access-token:TOKEN@github.com/Project-Tick/Project-Tick.git
# git remote add gitlab https://oauth2:TOKEN@gitlab.com/Project-Tick/Project-Tick.git
# git remote add codeberg https://TOKEN@codeberg.org/Project-Tick/Project-Tick.git
#
# Environment variables (optional):
# MIRROR_REMOTES — space-separated list of remote names to push to.
# Defaults to all configured mirror remotes.
# MIRROR_LOG — path to log file. Defaults to /var/log/git-mirror.log
# MIRROR_NOTIFY — email address for failure notifications (requires mail cmd)
#
# ==============================================================================
set -euo pipefail
# ---------------------
# Configuration
# ---------------------
# Where to find mirror remotes. Override with MIRROR_REMOTES env var.
# Falls back to auto-detecting all remotes that aren't "origin".
MIRROR_REMOTES="${MIRROR_REMOTES:-}"
MIRROR_LOG="${MIRROR_LOG:-/var/log/git-mirror.log}"
# Auto-detect remotes if not explicitly set
if [[ -z "$MIRROR_REMOTES" ]]; then
MIRROR_REMOTES=$(git remote | grep -v '^origin$' || true)
fi
if [[ -z "$MIRROR_REMOTES" ]]; then
echo "[mirror] No mirror remotes configured. Skipping." >&2
exit 0
fi
# ---------------------
# Logging
# ---------------------
log() {
local timestamp
timestamp="$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo "[$timestamp] $*" | tee -a "$MIRROR_LOG" 2>/dev/null || echo "[$timestamp] $*"
}
# ---------------------
# Main
# ---------------------
log "=== Mirror push triggered ==="
# Read the stdin from post-receive (old-sha new-sha refname)
REFS=()
while read -r oldrev newrev refname; do
REFS+=("$refname")
log " ref: $refname ($oldrev -> $newrev)"
done
FAILED_REMOTES=()
SUCCEEDED_REMOTES=()
for remote in $MIRROR_REMOTES; do
log "Pushing to remote: $remote"
# Use --mirror for full mirror, or --all --tags for selective
# --mirror pushes ALL refs (branches, tags, notes, etc.)
# --force ensures deleted branches/tags are also synced
if git push --mirror --force "$remote" 2>&1 | tee -a "$MIRROR_LOG" 2>/dev/null; then
SUCCEEDED_REMOTES+=("$remote")
log " ✓ Successfully pushed to $remote"
else
FAILED_REMOTES+=("$remote")
log " ✗ FAILED to push to $remote"
fi
done
log "--- Summary ---"
log " Succeeded: ${SUCCEEDED_REMOTES[*]:-none}"
log " Failed: ${FAILED_REMOTES[*]:-none}"
# Send notification on failure if configured
if [[ ${#FAILED_REMOTES[@]} -gt 0 && -n "${MIRROR_NOTIFY:-}" ]]; then
if command -v mail &>/dev/null; then
{
echo "Mirror push failed for the following remotes:"
printf ' - %s\n' "${FAILED_REMOTES[@]}"
echo ""
echo "Repository: $(pwd)"
echo "Refs updated:"
printf ' %s\n' "${REFS[@]}"
echo ""
echo "Check log: $MIRROR_LOG"
} | mail -s "[git-mirror] Push failure in $(basename "$(pwd)")" "$MIRROR_NOTIFY"
fi
fi
# Exit with error if any remote failed
if [[ ${#FAILED_REMOTES[@]} -gt 0 ]]; then
log "=== Finished with errors ==="
exit 1
fi
log "=== Finished successfully ==="
exit 0
|