#!/bin/sh # Checks the post-receive hooks under custom/hooks. The cache hook runs from a # copy pointed at a cache and a scan path in the trash directory, since the # shipped one names the system's own. The hooks run inside a repository's git # directory the way git runs them after a push, and once each installed and # pushed to. test_description='Check the shipped post-receive hooks' CGIT_TEST_NO_CREATE_REPOS=YesPlease . ./setup.sh HOOK_DIRECTORY="$TEST_OUTPUT_DIRECTORY/../custom/hooks" # The request each page slot in a directory answers, read off its head the # way the hook reads it, leaving out the locks and the cached repository # scan. cached_requests() { test -d "$1" || return 1 for slot in "$1"/* do case $slot in *.lock | */rc-*) continue ;; esac dd if="$slot" bs=4096 count=1 2>/dev/null >slot-head && tr "\0" "\n" cachedrc && for page in / /a.git/ /a.git/log/ /b.git/ /b.git/log/ do CGIT_CONFIG="$PWD/cachedrc" PATH_INFO="$page" QUERY_STRING= cgit >/dev/null || return 1 done && cgit_rc cachedrc "url=a.git/refs/" >/dev/null && cgit_rc cachedrc "url=b.git/refs/" >/dev/null && cgit_rc cachedrc "r=a.git&p=log" >/dev/null && cgit_rc cachedrc "r=b.git&p=log" >/dev/null && CGIT_CONFIG="$PWD/cachedrc" PATH_INFO=/foo+bar.git/log/ QUERY_STRING= cgit >/dev/null && cgit_rc cachedrc "url=foo%2bbar.git/refs/" >/dev/null && cgit_rc cachedrc "r=foo%2Bbar.git&p=log" >/dev/null && echo writer >slots/0123abcd.lock && printf "not a key\0page" >slots/00000000 && cp -R slots pristine && sort >expect-keys <<-\EOF && 7|http://0|1|/0| 7|http://0|7|/a.git/0| 7|http://0|11|/a.git/log/0| 7|http://0|7|/b.git/0| 7|http://0|11|/b.git/log/0| 7|http://0|0|15|url=a.git/refs/ 7|http://0|0|15|url=b.git/refs/ 7|http://0|0|13|r=a.git&p=log 7|http://0|0|13|r=b.git&p=log 7|http://0|17|/foo+bar.git/log/0| 7|http://0|0|23|url=foo%2bbar.git/refs/ 7|http://0|0|21|r=foo%2Bbar.git&p=log not a key EOF cached_requests pristine >keys && sort keys >sorted-keys && test_cmp expect-keys sorted-keys && ls pristine >pristine-slots && grep "^rc-" pristine-slots && sed -e "s|^cache_root=.*|cache_root='\''$PWD/slots'\''|" \ -e "s|^scan_path=.*|scan_path='\''$PWD/scan'\''|" \ "$HOOK_DIRECTORY/post-receive.cgit-cache" >cache-hook && grep -Fx "cache_root='\''$PWD/slots'\''" cache-hook && grep -Fx "scan_path='\''$PWD/scan'\''" cache-hook ' # With remove-suffix the url leaves out the .git the scan found, and the # hook has to leave it out the same way. test_expect_success 'set up a cache of repositories served without the suffix' ' mkdir suffix-slots && { echo "cache-root=$PWD/suffix-slots" && echo "cache-size=1000" && echo "virtual-root=/" && echo "remove-suffix=1" && echo "scan-path=$PWD/scan" } >suffixrc && for page in / /a/ /a/log/ /b/ /b/log/ do CGIT_CONFIG="$PWD/suffixrc" PATH_INFO="$page" QUERY_STRING= cgit >/dev/null || return 1 done && cp -R suffix-slots suffix-pristine && cached_requests suffix-pristine >keys && test_grep "|/a/log/0|" keys && test_grep "|/b/log/0|" keys && test_grep "|1|/0|" keys && sed -e "s|^cache_root=.*|cache_root='\''$PWD/suffix-slots'\''|" \ -e "s|^remove_suffix=.*|remove_suffix=1|" cache-hook >suffix-hook && grep -Fx "cache_root='\''$PWD/suffix-slots'\''" suffix-hook && grep -Fx "remove_suffix=1" suffix-hook ' # scan-path finds a repository with a work tree by its .git and serves it at # the work tree's path, while git runs its post-receive in the .git. test_expect_success 'set up a cache of a scanned repository with a work tree' ' git clone -q pusher scan/c && mkdir worktree-slots && sed "s|^cache-root=.*|cache-root=$PWD/worktree-slots|" cachedrc >worktreerc && for page in / /a.git/ /c/ /c/log/ do CGIT_CONFIG="$PWD/worktreerc" PATH_INFO="$page" QUERY_STRING= cgit >/dev/null || return 1 done && cp -R worktree-slots worktree-pristine && cached_requests worktree-pristine >keys && test_grep "|/c/0|" keys && test_grep "|/c/log/0|" keys && test_grep "|/a.git/0|" keys && sed "s|^cache_root=.*|cache_root='\''$PWD/worktree-slots'\''|" cache-hook >worktree-hook && grep -Fx "cache_root='\''$PWD/worktree-slots'\''" worktree-hook ' # The hooks are POSIX sh, so every case runs under each such shell that is # here, once however many names reach it. Whatever a hook prints reaches the # pusher, so each has to stay silent. shells= shell_paths= for shell in sh dash ksh mksh yash posh bash zsh busybox do shell_path=$(command -v $shell) || continue for seen in $shell_paths do test "$seen" -ef "$shell_path" && continue 2 done shell_paths="$shell_paths $shell_path" shells="$shells $shell" done say "running the hooks under$shells" # Every case below needs the set-up tests above it, and some need the case # before them under the same shell, so a --run subset has to take those too. for shell in $shells do # zsh and busybox behave as sh only when asked to. case $shell in zsh) run="zsh --emulate sh" ;; busybox) run="busybox sh" ;; *) run=$shell ;; esac test_expect_success "a push drops its own pages and the index under $shell" ' fresh_slots && (cd scan/a.git && $run ../../cache-hook 2>../../err) && test_must_be_empty err && cached_requests slots >left && test_grep "|/b.git/log/" left && test_grep "url=b.git/refs/" left && test_grep "r=b.git&p=log" left && test_grep ! "a.git" left && test_grep ! "1|/0|" left && test_grep ! "not a key" left && test_path_is_file slots/0123abcd.lock && ls slots >left-slots && test_grep ! "^rc-" left-slots ' test_expect_success "a push drops the pages of a repository served without the suffix under $shell" ' rm -rf suffix-slots && cp -R suffix-pristine suffix-slots && (cd scan/a.git && $run ../../suffix-hook 2>../../err) && test_must_be_empty err && cached_requests suffix-slots >left && test_grep "|/b/log/0|" left && test_grep ! "|/a/" left && test_grep ! "|1|/0|" left ' test_expect_success "a push to a repository with a work tree drops its pages under $shell" ' rm -rf worktree-slots && cp -R worktree-pristine worktree-slots && (cd scan/c/.git && $run ../../../worktree-hook 2>../../../err) && test_must_be_empty err && cached_requests worktree-slots >left && test_grep "|/a.git/0|" left && test_grep ! "|/c/" left ' test_expect_success "a push drops the pages of a repository whose url is escaped under $shell" ' fresh_slots && (cd scan/foo+bar.git && $run ../../cache-hook 2>../../err) && test_must_be_empty err && cached_requests slots >left && test_grep "|/b.git/log/" left && test_grep "url=b.git/refs/" left && test_grep ! "foo" left ' test_expect_success "a repository named in cgitrc gives the hook its url under $shell" ' fresh_slots && git -C scan/b.git config hooks.cgiturl b.git && sed "s|^scan_path=.*|scan_path=/nonexistent|" cache-hook >named-hook && grep -Fx "scan_path=/nonexistent" named-hook && (cd scan/b.git && $run ../../named-hook 2>../../err) && test_must_be_empty err && cached_requests slots >left && test_grep "|/a.git/log/" left && test_grep ! "b.git" left ' test_expect_success "with no url to go on the whole cache goes under $shell" ' fresh_slots && (cd scan/a.git && $run ../../named-hook 2>../../err) && test_must_be_empty err && test "$(ls slots)" = 0123abcd.lock ' test_expect_success "the cache hook does nothing without a cache root under $shell" ' sed "s|^cache_root=.*|cache_root='\''$PWD/none'\''|" cache-hook >rootless-hook && grep -Fx "cache_root='\''$PWD/none'\''" rootless-hook && (cd scan/a.git && $run ../../rootless-hook 2>../../err) && test_must_be_empty err && test_path_is_missing none ' test_expect_success SANITY "the cache hook shows a pusher no error under $shell" ' fresh_slots && test_when_finished "chmod 755 slots" && chmod 555 slots && (cd scan/a.git && $run ../../cache-hook 2>../../err) && test_must_be_empty err && ls slots >left && test_cmp pristine-slots left ' done # The newest branch tip by committer date is main. The other branch has the # newer author date, a tag outside refs/heads points at a newer commit # still, and an annotated tag was made later than all of them. test_expect_success 'set up a repository whose dates disagree' ' test_create_repo work && ( cd work && echo one >file && git add file && GIT_AUTHOR_DATE="2001-01-01 00:00:00 +0000" \ GIT_COMMITTER_DATE="2021-02-03 04:05:06 +0000" git commit -q -m newest && git branch -M main && git checkout -q -b old && echo two >file && GIT_AUTHOR_DATE="2029-01-01 00:00:00 +0000" \ GIT_COMMITTER_DATE="2019-01-01 00:00:00 +0000" git commit -q -a -m older && echo three >file && GIT_COMMITTER_DATE="2025-01-01 00:00:00 +0000" git commit -q -a -m loose && git tag loose && git reset -q --hard HEAD^ && GIT_COMMITTER_DATE="2027-01-01 00:00:00 +0000" git tag -a -m release release main ) && git init -q --bare plain.git && git -C work push -q ../plain.git main old loose release && echo "2021-02-03 04:05:06 +0000" >expect && echo last-modified >expect-entries && echo kept >kept ' for shell in $shells do test_expect_success "the age hook writes the newest branch commit date under $shell" ' rm -f plain.git/info/web/last-modified && (cd plain.git && $run "$HOOK_DIRECTORY/post-receive.cgit-age" 2>../err) && test_must_be_empty err && test_cmp expect plain.git/info/web/last-modified && ls plain.git/info/web >entries && test_cmp expect-entries entries ' test_expect_success "cgit shows the date the age hook wrote as the idle time under $shell" ' cgitrc_repo agerc plain "repo.path=$PWD/plain.git" && cgit_ok agerc "" >index && test_grep "