vibe coded a little

This commit is contained in:
2026-10-23 11:28:30 -04:00
parent cc016dab8c
commit 8ee78b4aac
3 changed files with 136 additions and 33 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
# rootfs will contain all binaries given by toolchain # rootfs will contain all binaries given by toolchain
toolchain = pkgs.buildEnv { toolchain = pkgs.buildEnv {
name = "pigeon-toolchain"; name = "pigeon-toolchain";
paths = with pkgs; [ bash coreutils gcc gnumake util-linux ]; paths = with pkgs; [ binutils bash coreutils gcc gnumake util-linux ];
}; };
closure = pkgs.closureInfo { closure = pkgs.closureInfo {
rootPaths = [ toolchain ]; rootPaths = [ toolchain ];
@@ -77,7 +77,7 @@
pigeon = pkgs.writeShellApplication { pigeon = pkgs.writeShellApplication {
name = "pigeon"; name = "pigeon";
runtimeInputs = with pkgs; [ coreutils curl e2fsprogs firecracker git ]; runtimeInputs = with pkgs; [ coreutils curl e2fsprogs firecracker gcc git gnumake ];
text = builtins.replaceStrings text = builtins.replaceStrings
[ "@kernel@" "@rootfs@" ] [ "${kernel}" "${rootfs}"] [ "@kernel@" "@rootfs@" ] [ "${kernel}" "${rootfs}"]
(builtins.readFile ./pigeon.sh); (builtins.readFile ./pigeon.sh);
+1 -1
View File
@@ -22,7 +22,6 @@ cd /tmp
make >/root/results/build.log 2>&1 make >/root/results/build.log 2>&1
echo $? >/root/results/build.status echo $? >/root/results/build.status
) )
[ "$(cat /root/results/build.status)" != 0 ] && exit 1
for TEST in /root/project/tests/*.sh ; do for TEST in /root/project/tests/*.sh ; do
( (
@@ -30,5 +29,6 @@ for TEST in /root/project/tests/*.sh ; do
timeout -s 9 2 \ timeout -s 9 2 \
setpriv --reuid=1 --regid=1 --clear-groups --no-new-privs --inh-caps=-all \ setpriv --reuid=1 --regid=1 --clear-groups --no-new-privs --inh-caps=-all \
"$TEST" >"/root/results/$(basename "$TEST" .sh)" 2>/dev/null "$TEST" >"/root/results/$(basename "$TEST" .sh)" 2>/dev/null
echo $? >"/root/results/$(basename "$TEST" .sh).status"
) )
done done
+133 -30
View File
@@ -12,6 +12,7 @@ RELEASE_TIME="$6"
RATE_LIMIT=3 RATE_LIMIT=3
PIGEON_DATABASE="$(realpath -m "$PIGEON_STATE/db")" PIGEON_DATABASE="$(realpath -m "$PIGEON_STATE/db")"
PIGEON_PROJECTS="$(realpath -m "$PIGEON_PROJECTS")"
RELEASE_TIME="$(date -d "$RELEASE_TIME" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || true)" RELEASE_TIME="$(date -d "$RELEASE_TIME" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || true)"
report() { report() {
@@ -21,6 +22,7 @@ report() {
--data "$(jq -n --arg d "$1" '{"description": $d}')" \ --data "$(jq -n --arg d "$1" '{"description": $d}')" \
--url "https://gitlab.com/api/v4/projects/$PROJECT_ID/releases/$RELEASE_TAG" --url "https://gitlab.com/api/v4/projects/$PROJECT_ID/releases/$RELEASE_TAG"
} }
# shellcheck disable=SC2120,SC2119
db() { db() {
sqlite3 -bail -cmd '.timeout 10000' "$PIGEON_DATABASE" "$@" sqlite3 -bail -cmd '.timeout 10000' "$PIGEON_DATABASE" "$@"
} }
@@ -35,26 +37,11 @@ CREATE TABLE IF NOT EXISTS results (
project TEXT NOT NULL, project TEXT NOT NULL,
hash TEXT NOT NULL, hash TEXT NOT NULL,
time TEXT NOT NULL DEFAULT (datetime('now')), time TEXT NOT NULL DEFAULT (datetime('now')),
score INTEGER CHECK (score >= 0) score INTEGER CHECK (score >= 0),
token INTEGER NOT NULL
); );
EOF EOF
ROW="$(db <<EOF
INSERT INTO results (student, project, hash, time)
SELECT $(q "$PROJECT_NAMESPACE"), $(q "$PROJECT_NAME"), $(q "$RELEASE_COMMIT"),
COALESCE(NULLIF($(q "$RELEASE_TIME"), ''), datetime('now'))
WHERE (SELECT COUNT(*) FROM results
WHERE student = $(q "$PROJECT_NAMESPACE") AND project = $(q "$PROJECT_NAME")
AND time > datetime('now', '-1 day')) < $RATE_LIMIT
RETURNING id;
EOF
)"
if [ -z "$ROW" ] ; then
report "Rate limit exceeded!"
exit
fi
DIRECTORY="$(mktemp -d /tmp/pigeon.XXXXXXXXXX)" DIRECTORY="$(mktemp -d /tmp/pigeon.XXXXXXXXXX)"
trap 'rm -rf "$DIRECTORY"' EXIT trap 'rm -rf "$DIRECTORY"' EXIT
cd "$DIRECTORY" cd "$DIRECTORY"
@@ -63,10 +50,23 @@ git -c advice.detachedHead=false clone -q --rev "$RELEASE_COMMIT" --depth 1 \
"https://oauth2:$PIGEON_GITLAB_TOKEN@gitlab.com/$PROJECT_NAMESPACE/$PROJECT_NAME.git" student/ "https://oauth2:$PIGEON_GITLAB_TOKEN@gitlab.com/$PROJECT_NAMESPACE/$PROJECT_NAME.git" student/
rm -rf student/.git rm -rf student/.git
TESTS="$PIGEON_PROJECTS/$PROJECT_NAME/tests"
mkdir -p project/tests project/build mkdir -p project/tests project/build
cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/tests/*.sh project/tests/ cp -a "$TESTS"/*.sh project/tests/
cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/build/. project/build/ cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/build/. project/build/
# A project may ship an answer-key solution. It is built and run only here,
# on the host, and never copied onto any drive the guest can reach -- tests
# compare the student's reported output against it after the VM has exited.
CANONICAL=""
if [ -d "$PIGEON_PROJECTS/$PROJECT_NAME/canonical" ] ; then
mkdir -p canonical
cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/build/. canonical/
cp -a --remove-destination "$PIGEON_PROJECTS/$PROJECT_NAME"/canonical/. canonical/
( cd canonical && timeout 30 make >/dev/null 2>&1 ) || true
[ -x canonical/program ] && CANONICAL="$(realpath canonical/program)"
fi
mkfs.ext4 -q -F -d student student.img 16M mkfs.ext4 -q -F -d student student.img 16M
mkfs.ext4 -q -F -d project project.img 16M mkfs.ext4 -q -F -d project project.img 16M
mkfs.ext4 -q -F results.img 16M mkfs.ext4 -q -F results.img 16M
@@ -89,21 +89,124 @@ EOF
firecracker --no-api --level Error --config-file vm.json firecracker --no-api --level Error --config-file vm.json
RESULT="" PUBLIC_SCORE=0; PUBLIC_TOTAL=0
SCORE=0 RELEASE_SCORE=0; RELEASE_TOTAL=0
TESTS="$PIGEON_PROJECTS/$PROJECT_NAME/tests" SECRET_SCORE=0; SECRET_TOTAL=0
NAMES=()
STATUSES=()
ANSWERS=()
QUESTIONS=()
TIERS=()
for TEST in "$TESTS"/*.sh ; do for TEST in "$TESTS"/*.sh ; do
debugfs -R "cat /$(basename "$TEST" .sh)" results.img >scratch 2>/dev/null NAME="$(basename "$TEST" .sh)"
if cmp -s scratch "$TESTS/$(basename "$TEST" .sh).expected" ; then TIER="${NAME%%-*}"
RESULT="${RESULT}PASS\n" QUESTION="$(cat "$TESTS/$NAME.question")"
SCORE=$((SCORE + 1)) debugfs -R "cat /$NAME" results.img >scratch 2>/dev/null
EXIT="$(debugfs -R "cat /$NAME.status" results.img 2>/dev/null)"
case "$EXIT" in '' | *[!0-9]*) EXIT=0 ;; esac
# A test may expect the student's program to exit with a specific status
# (e.g. 134 for a SIGABRT from a correctly-triggered assert()) instead of
# the default 0.
EXPECTED_EXIT=0
[ -f "$TESTS/$NAME.exit" ] && EXPECTED_EXIT="$(cat "$TESTS/$NAME.exit")"
if [ -f "$TESTS/$NAME.expected" ] ; then
if cmp -s scratch "$TESTS/$NAME.expected" && [ "$EXIT" -eq "$EXPECTED_EXIT" ] ; then
MATCH=1
else
MATCH=0
fi
elif [ -n "$CANONICAL" ] ; then
# No static .expected: the test reported its own "ARG<TAB>OUTPUT" pairs,
# so check each one against the canonical solution run here on the host.
MATCH=1
while IFS="$(printf '\t')" read -r ARG OUT ; do
[ -n "$ARG" ] || continue
EXPECTED="$("$CANONICAL" "$ARG" 2>/dev/null)"
[ "$OUT" = "$EXPECTED" ] || MATCH=0
done <scratch
else else
RESULT="${RESULT}FAIL: $(cat "$TESTS/$(basename "$TEST" .sh).hint")\n" MATCH=0
fi fi
if [ "$MATCH" = 1 ] ; then
STATUS=PASS
ANSWER=YES
else
STATUS=FAIL
# timeout -s 9 (used in init.sh) exits 137 (128+SIGKILL) on a timeout,
# not the 124 it uses for its default signal (SIGTERM). Timing out is
# never the expected outcome, so it's checked before EXPECTED_EXIT.
if [ "$EXIT" -eq 124 ] || [ "$EXIT" -eq 137 ] ; then
ANSWER="NO (timed out)"
elif [ "$EXIT" -eq "$EXPECTED_EXIT" ] ; then
ANSWER="NO (wrong output)"
elif [ "$EXIT" -gt 128 ] ; then
ANSWER="NO (crashed, signal $((EXIT - 128)))"
else
ANSWER="NO (exited with status $EXIT, expected $EXPECTED_EXIT)"
fi
fi
NAMES+=("$NAME")
STATUSES+=("$STATUS")
ANSWERS+=("$ANSWER")
QUESTIONS+=("$QUESTION")
TIERS+=("$TIER")
case "$TIER" in
public)
PUBLIC_TOTAL=$((PUBLIC_TOTAL + 1))
[ "$STATUS" = PASS ] && PUBLIC_SCORE=$((PUBLIC_SCORE + 1))
;;
release)
RELEASE_TOTAL=$((RELEASE_TOTAL + 1))
[ "$STATUS" = PASS ] && RELEASE_SCORE=$((RELEASE_SCORE + 1))
;;
secret)
SECRET_TOTAL=$((SECRET_TOTAL + 1))
[ "$STATUS" = PASS ] && SECRET_SCORE=$((SECRET_SCORE + 1))
;;
esac
done done
# Record before reporting, so a GitLab outage doesn't lose the result. PUBLIC_PASS=0
db "UPDATE results SET score = $SCORE WHERE id = $ROW;" || [ "$PUBLIC_SCORE" -eq "$PUBLIC_TOTAL" ] && PUBLIC_PASS=1
echo "pigeon: failed to record score for row $ROW in $PIGEON_DB" >&2 SCORE=$((PUBLIC_SCORE + RELEASE_SCORE + SECRET_SCORE))
report "$RESULT" TOKEN="$(db <<EOF
INSERT INTO results (student, project, hash, time, score, token)
SELECT $(q "$PROJECT_NAMESPACE"), $(q "$PROJECT_NAME"), $(q "$RELEASE_COMMIT"),
COALESCE(NULLIF($(q "$RELEASE_TIME"), ''), datetime('now')),
$SCORE,
CASE WHEN $PUBLIC_PASS = 1 AND (
SELECT COUNT(*) FROM results
WHERE student = $(q "$PROJECT_NAMESPACE") AND project = $(q "$PROJECT_NAME")
AND token = 1 AND time > datetime('now', '-1 day')
) < $RATE_LIMIT THEN 1 ELSE 0 END
RETURNING token;
EOF
)"
SHOWN_QUESTIONS=()
SHOWN_ANSWERS=()
for i in "${!NAMES[@]}" ; do
case "${TIERS[$i]}" in
public) ;;
release) [ "$TOKEN" = 1 ] || continue ;;
*) continue ;;
esac
SHOWN_QUESTIONS+=("${QUESTIONS[$i]}")
SHOWN_ANSWERS+=("${ANSWERS[$i]}")
done
MESSAGE=""
for i in "${!SHOWN_QUESTIONS[@]}" ; do
MESSAGE="${MESSAGE}${SHOWN_QUESTIONS[$i]} ${SHOWN_ANSWERS[$i]} "$'\n'
done
if [ "$TOKEN" != 1 ] && [ "$PUBLIC_PASS" = 1 ] ; then
MESSAGE="${MESSAGE}"$'\n'"All public tests passed, but you have used all $RATE_LIMIT submissions for today. Release test results are withheld until tomorrow."
fi
report "$MESSAGE"