#!/bin/sh
# Serves a real repository from a work clone and clones it back with git,
# so a change to how a reply reaches a client is judged by a client.
#
# sh devlog/check_git_clone.sh
#
# Unit cases cannot catch what breaks a git clone: a missing status line, a
# content length that does not match the body, a request that runs on past
# where it should end. Each of those shipped once because the only evidence
# was a passing case. Run this before cutting any patch that touches how a
# response is emitted, and paste what it prints.
#
# The script leaves the server running so a following command can use it,
# and prints where the repository was put.
WORK=${WORK:-/home/claude/work}
SEED=${SEED:-/tmp/seed_repo}
OUT=${OUT:-/tmp/cloned_back}
PAGE=${PAGE:-CheckRepo}
COMMITS=${COMMITS:-6}
BYTES_PER_FILE=${BYTES_PER_FILE:-8000000}
cd "$WORK" || exit 1
if [ ! -d "$SEED/.git" ]; then
rm -rf "$SEED"
mkdir -p "$SEED"
cd "$SEED" || exit 1
git init -q .
git config user.email check@dev.null
git config user.name Check
# Enough content that the reply cannot be sent in one piece and the
# draining, the window credit and the running-time limit are all
# exercised. A small repository passed while a large one failed.
n=1
while [ "$n" -le "$COMMITS" ]; do
head -c "$BYTES_PER_FILE" /dev/urandom | base64 > "file$n.txt"
git add . > /dev/null
git -c commit.gpgsign=false commit -q -m "commit $n"
n=`expr $n + 1`
done
cd "$WORK" || exit 1
fi
php -r '
require "'"$WORK"'/src/library/Utility.php";
$gm = new seekquarry\yioop\models\GroupModel();
$gm->setPageName(1, 2, "'"$PAGE"'", "words", "en-US", "made", "t", "a", "",
true, time(), time());
$page = $gm->getPageId(2, "'"$PAGE"'", "en-US");
$folders = $gm->getGroupPageResourcesFolders(2, $page, "", true);
$row = $gm->db->fetchArray($gm->db->execute(
"SELECT PAGE FROM GROUP_PAGE WHERE ID=?", [$page]));
$at = strpos($row["PAGE"], "END_HEAD_VARS");
$head = substr($row["PAGE"], 0, $at);
$head = preg_replace("/page_type=[a-z_]*/", "page_type=git_repository",
$head);
if (strpos($head, "page_type") === false) {
$head .= "page_type=git_repository\n";
}
$gm->db->execute("UPDATE GROUP_PAGE SET PAGE=? WHERE ID=?",
[$head . substr($row["PAGE"], $at), $page]);
echo $folders[0];
' > /tmp/check_repo_folder.txt 2>/dev/null
folder=`cat /tmp/check_repo_folder.txt`
if [ -z "$folder" ]; then
echo "check: could not make the repository page"
exit 1
fi
echo "repository folder: $folder"
cp -r "$SEED"/.git/* "$folder"/ 2>/dev/null
# A server left on the port answers with the code it loaded at startup and
# hides the change under test, which cost several rounds once. Free the
# ports and wait until nothing holds them before starting a fresh one.
pkill -9 -f "php index.php" 2>/dev/null
fuser -k 443/tcp 2>/dev/null
fuser -k 80/tcp 2>/dev/null
sleep 3
holding=`fuser 443/tcp 2>/dev/null | wc -w`
if [ "$holding" -gt 0 ]; then
echo "check: something still holds port 443; nothing was tested"
exit 1
fi
setsid sh -c "cd $WORK && timeout -s KILL 600 php index.php secure-start \
</dev/null >/tmp/check_server.log 2>&1" &
waited=0
while [ "$waited" -lt 40 ]; do
sleep 2
waited=`expr $waited + 2`
code=`curl -sk --max-time 5 -o /dev/null -w "%{http_code}" \
https://localhost/ 2>/dev/null`
if [ "$code" = "200" ]; then
break
fi
done
echo "server answered: $code after ${waited}s, pid `pgrep -f \
'php index.php' | head -1`"
if [ "$code" != "200" ]; then
echo "check: the server never answered; nothing was tested"
exit 1
fi
echo "advertisement: `curl -sk --max-time 15 -o /dev/null \
-w '%{http_code} %{size_download} bytes' \
\"https://localhost/group/2/$PAGE.git/info/refs?service=git-upload-pack\"`"
rm -rf "$OUT"
git -c http.sslVerify=false clone "https://localhost/group/2/$PAGE.git" \
"$OUT" > /tmp/check_clone.txt 2>&1
if [ ! -d "$OUT/.git" ]; then
echo "clone: FAIL"
tail -4 /tmp/check_clone.txt
exit 1
fi
echo "clone: PASS, `ls \"$OUT\" | wc -l` files, `cd \"$OUT\" && \
git log --oneline | wc -l` commits"
# A pull is a fetch with what the client already holds, which asks the
# server for a smaller pack built against those; a fresh clone never
# exercises that. Add a commit to what is served, then pull it.
cd "$SEED" || exit 1
echo "another line" >> pulled_change.txt
git add . > /dev/null
git -c commit.gpgsign=false commit -q -m "a change to pull"
cp -r "$SEED"/.git/* "$folder"/ 2>/dev/null
cd "$OUT" || exit 1
git -c http.sslVerify=false pull > /tmp/check_pull.txt 2>&1
if grep -q "pulled_change" /tmp/check_pull.txt ||
[ -f "$OUT/pulled_change.txt" ]; then
echo "pull: PASS"
exit 0
fi
echo "pull: FAIL"
tail -4 /tmp/check_pull.txt
exit 1