fix(git-gate): forward push options
test / unit (pull_request) Successful in 29s
test / integration (pull_request) Successful in 17s
lint / lint (push) Successful in 1m28s
test / unit (push) Successful in 29s
test / integration (push) Successful in 16s
Update Quality Badges / update-badges (push) Successful in 1m7s
test / unit (pull_request) Successful in 29s
test / integration (pull_request) Successful in 17s
lint / lint (push) Successful in 1m28s
test / unit (push) Successful in 29s
test / integration (push) Successful in 16s
Update Quality Badges / update-badges (push) Successful in 1m7s
This commit was merged in pull request #225.
This commit is contained in:
+17
-1
@@ -204,6 +204,7 @@ def git_gate_render_entrypoint(upstreams: tuple[GitGateUpstream, ...]) -> str:
|
||||
" git -C \"$repo\" config git-gate.identityFile \"$keyfile\"",
|
||||
" git -C \"$repo\" config git-gate.knownHosts \"$hostsfile\"",
|
||||
" git -C \"$repo\" config receive.denyCurrentBranch ignore",
|
||||
" git -C \"$repo\" config receive.advertisePushOptions true",
|
||||
" git -C \"$repo\" config http.receivepack true",
|
||||
" install -m 755 /etc/git-gate/pre-receive \"$repo/hooks/pre-receive\"",
|
||||
"}",
|
||||
@@ -280,6 +281,21 @@ if [ ! -f "$hostsfile" ]; then
|
||||
fi
|
||||
ssh_cmd="ssh -i $keyfile -o UserKnownHostsFile=$hostsfile -o StrictHostKeyChecking=yes -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10"
|
||||
|
||||
push_option_count=${GIT_PUSH_OPTION_COUNT:-0}
|
||||
case "$push_option_count" in
|
||||
''|*[!0-9]*)
|
||||
echo "git-gate: invalid GIT_PUSH_OPTION_COUNT=$push_option_count" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
set --
|
||||
i=0
|
||||
while [ "$i" -lt "$push_option_count" ]; do
|
||||
opt=$(printenv "GIT_PUSH_OPTION_$i" || :)
|
||||
set -- "$@" --push-option="$opt"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
while IFS=' ' read -r old new ref; do
|
||||
[ -z "$ref" ] && continue
|
||||
if [ "$new" = "$zero" ]; then
|
||||
@@ -288,7 +304,7 @@ while IFS=' ' read -r old new ref; do
|
||||
refspec="$new:$ref"
|
||||
fi
|
||||
echo "git-gate: forwarding $ref to origin" >&2
|
||||
if ! GIT_SSH_COMMAND="$ssh_cmd" git push origin "$refspec" 1>&2; then
|
||||
if ! GIT_SSH_COMMAND="$ssh_cmd" git push "$@" origin "$refspec" 1>&2; then
|
||||
echo "git-gate: upstream push failed for $ref" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -98,6 +98,9 @@ class TestEntrypointRender(unittest.TestCase):
|
||||
# Smart HTTP receive-pack uses the same bare repos and hooks
|
||||
# as git-daemon, so repos must opt in to HTTP pushes too.
|
||||
self.assertIn("http.receivepack true", script)
|
||||
# The gate must advertise push-option support so clients can
|
||||
# pass forge-specific options through to the pre-receive hook.
|
||||
self.assertIn("receive.advertisePushOptions true", script)
|
||||
# The access-hook is what makes fetch a mirror operation
|
||||
# against the upstream (PRD 0008 v1.1).
|
||||
self.assertIn("--access-hook=/etc/git-gate/access-hook", script)
|
||||
@@ -153,7 +156,8 @@ class TestHookRender(unittest.TestCase):
|
||||
hook = git_gate_render_hook()
|
||||
# Phase 1: gitleaks. Phase 2: forward to origin.
|
||||
self.assertIn("gitleaks git", hook)
|
||||
self.assertIn("git push origin", hook)
|
||||
self.assertIn("git push", hook)
|
||||
self.assertIn("origin \"$refspec\"", hook)
|
||||
# KnownHostKey absence is fail-closed.
|
||||
self.assertIn("refusing to push", hook)
|
||||
# Stdin is buffered to a tempfile so both phases can re-read.
|
||||
@@ -177,6 +181,17 @@ class TestHookRender(unittest.TestCase):
|
||||
self.assertIn("BatchMode=yes", hook)
|
||||
self.assertIn("ConnectTimeout=", hook)
|
||||
|
||||
def test_forward_preserves_push_options(self):
|
||||
# Git exposes push options to pre-receive hooks as
|
||||
# GIT_PUSH_OPTION_COUNT + indexed GIT_PUSH_OPTION_N variables.
|
||||
# Forward them as first-class argv entries so spaces and shell
|
||||
# metacharacters inside option values remain data.
|
||||
hook = git_gate_render_hook()
|
||||
self.assertIn("push_option_count=${GIT_PUSH_OPTION_COUNT:-0}", hook)
|
||||
self.assertIn('opt=$(printenv "GIT_PUSH_OPTION_$i" || :)', hook)
|
||||
self.assertIn('set -- "$@" --push-option="$opt"', hook)
|
||||
self.assertIn('git push "$@" origin "$refspec"', hook)
|
||||
|
||||
|
||||
class TestAccessHookRender(unittest.TestCase):
|
||||
def test_access_hook_refreshes_origin_on_upload_pack(self):
|
||||
|
||||
Reference in New Issue
Block a user