Files
Pigeon/pigeon.sh
T

110 lines
3.4 KiB
Bash
Raw Normal View History

2026-10-16 01:18:37 -04:00
PROJECT_ID="$1"
PROJECT_NAMESPACE="$2"
PROJECT_NAME="$3"
RELEASE_COMMIT="$4"
RELEASE_TAG="$5"
RELEASE_TIME="$6"
2026-10-15 20:33:59 -04:00
: "${PIGEON_GITLAB_TOKEN:?}"
: "${PIGEON_PROJECTS:?}"
2026-10-16 01:18:37 -04:00
: "${PIGEON_STATE:?}"
2026-10-15 23:45:11 -04:00
2026-10-16 01:18:37 -04:00
RATE_LIMIT=3
PIGEON_DATABASE="$(realpath -m "$PIGEON_STATE/db")"
RELEASE_TIME="$(date -d "$RELEASE_TIME" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || true)"
2026-10-15 23:45:11 -04:00
report() {
curl -s --show-error --out-null --request PUT \
--header "PRIVATE-TOKEN: $PIGEON_GITLAB_TOKEN" \
--header 'Content-Type: application/json' \
2026-10-16 01:18:37 -04:00
--data "$(jq -n --arg d "$1" '{"description": $d}')" \
--url "https://gitlab.com/api/v4/projects/$PROJECT_ID/releases/$RELEASE_TAG"
}
db() {
sqlite3 -bail -cmd '.timeout 10000' "$PIGEON_DATABASE" "$@"
}
q() {
printf "'%s'" "$(printf %s "$1" | sed "s/'/''/g")"
2026-10-15 23:45:11 -04:00
}
db << 'EOF'
CREATE TABLE IF NOT EXISTS results (
id INTEGER PRIMARY KEY,
student TEXT NOT NULL,
project TEXT NOT NULL,
2026-10-16 01:18:37 -04:00
hash TEXT NOT NULL,
2026-10-15 23:45:11 -04:00
time TEXT NOT NULL DEFAULT (datetime('now')),
score INTEGER CHECK (score >= 0)
);
EOF
2026-10-16 01:18:37 -04:00
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
)"
2026-10-15 23:45:11 -04:00
2026-10-16 01:18:37 -04:00
if [ -z "$ROW" ] ; then
report "Rate limit exceeded!"
exit
2026-10-15 23:45:11 -04:00
fi
2026-10-15 20:33:59 -04:00
DIRECTORY="$(mktemp -d /tmp/pigeon.XXXXXXXXXX)"
trap 'rm -rf "$DIRECTORY"' EXIT
cd "$DIRECTORY"
2026-10-16 01:18:37 -04:00
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/
2026-10-15 20:33:59 -04:00
rm -rf student/.git
mkdir -p project/tests project/build
2026-10-16 01:18:37 -04:00
cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/tests/*.sh project/tests/
cp -a "$PIGEON_PROJECTS/$PROJECT_NAME"/build/. project/build/
2026-10-15 20:33:59 -04:00
mkfs.ext4 -q -F -d student student.img 16M
mkfs.ext4 -q -F -d project project.img 16M
mkfs.ext4 -q -F results.img 16M
cat >vm.json <<'EOF'
{
"boot-source": {
"kernel_image_path": "@kernel@",
"boot_args": "reboot=k panic=1 init=/init quiet nomodule i8042.noaux i8042.nomux i8042.dumbkbd swiotlb=noforce console=ttyS0"
},
"drives": [
{ "drive_id": "rootfs", "path_on_host": "@rootfs@", "is_root_device": true, "is_read_only": true },
{ "drive_id": "student", "path_on_host": "student.img", "is_root_device": false, "is_read_only": true },
{ "drive_id": "project", "path_on_host": "project.img", "is_root_device": false, "is_read_only": true },
{ "drive_id": "results", "path_on_host": "results.img", "is_root_device": false, "is_read_only": false }
],
"machine-config": { "vcpu_count": 1, "mem_size_mib": 512 }
}
EOF
firecracker --no-api --level Error --config-file vm.json
RESULT=""
2026-10-15 23:45:11 -04:00
SCORE=0
2026-10-16 01:18:37 -04:00
TESTS="$PIGEON_PROJECTS/$PROJECT_NAME/tests"
2026-10-15 20:33:59 -04:00
for TEST in "$TESTS"/*.sh ; do
debugfs -R "cat /$(basename "$TEST" .sh)" results.img >scratch 2>/dev/null
2026-10-15 23:45:11 -04:00
if cmp -s scratch "$TESTS/$(basename "$TEST" .sh).expected" ; then
2026-10-15 20:33:59 -04:00
RESULT="${RESULT}PASS\n"
2026-10-15 23:45:11 -04:00
SCORE=$((SCORE + 1))
2026-10-15 20:33:59 -04:00
else
RESULT="${RESULT}FAIL: $(cat "$TESTS/$(basename "$TEST" .sh).hint")\n"
fi
done
2026-10-15 23:45:11 -04:00
# Record before reporting, so a GitLab outage doesn't lose the result.
db "UPDATE results SET score = $SCORE WHERE id = $ROW;" ||
echo "pigeon: failed to record score for row $ROW in $PIGEON_DB" >&2
report "$RESULT"