initial commit

This commit is contained in:
2026-10-15 20:33:59 -04:00
commit 8b75509e4b
5 changed files with 259 additions and 0 deletions
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1791734076,
"narHash": "sha256-i5qk1kLiit98rRbh1AXmWBNT9QXC/y4l5Wk/u5EdpDQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7fd4004d0511991fff3a0524856a9be09e28c5f0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+100
View File
@@ -0,0 +1,100 @@
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }:
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
toolchain = pkgs.buildEnv {
name = "pigeon-toolchain";
paths = with pkgs; [ bash coreutils gcc gnumake util-linux ];
};
closure = pkgs.closureInfo {
rootPaths = [ toolchain ];
};
rootfs = pkgs.runCommand "pigeon-rootfs" {
nativeBuildInputs = with pkgs; [ e2fsprogs ];
} ''
mkdir -p rootfs && cd rootfs
umask 022
mkdir -p dev proc sys tmp usr nix/store root/student root/project root/results
chmod 777 tmp
for FILE in $(cat ${closure}/store-paths) ; do
cp -a "$FILE" nix/store
done
ln -s ${toolchain}/bin bin
ln -s ${toolchain}/bin usr/bin
cp ${./init.sh} init
cd ..
mkfs.ext4 -q -F -d rootfs $out "1G"
'';
kernel-params = [
"64BIT" "TTY" "SERIAL_8250" "SERIAL_8250_CONSOLE" "PRINTK"
"HYPERVISOR_GUEST" "PARAVIRT" "KVM_GUEST"
"BLOCK" "VIRTIO_MENU" "VIRTIO" "VIRTIO_PCI" "VIRTIO_MMIO" "VIRTIO_BLK"
"EXT4_FS" "SHMEM" "TMPFS" "PROC_FS" "PROC_SYSCTL" "SYSFS" "DEVTMPFS"
"ACPI" "PCI" "MAGIC_SYSRQ"
"BINFMT_ELF" "BINFMT_SCRIPT" "MULTIUSER" "FUTEX" "EPOLL" "EVENTFD"
"SIGNALFD" "TIMERFD" "POSIX_TIMERS" "FILE_LOCKING" "ADVISE_SYSCALLS"
"NET" "UNIX"
];
kernel = pkgs.stdenv.mkDerivation {
inherit (pkgs.linux_6_1) src version;
pname = "pigeon-kernel";
nativeBuildInputs = with pkgs; [ bc bison elfutils flex perl ];
hardeningDisable = [ "all" ];
enableParallelBuilding = true;
configurePhase = ''
runHook preConfigure
patchShebangs scripts tools
make tinyconfig
for OPTION in ${toString kernel-params} ; do
scripts/config --enable "$OPTION"
done
make olddefconfig
for OPTION in ${toString kernel-params} ; do
grep -qx "CONFIG_$OPTION=y" .config || exit 1
done
runHook postConfigure
'';
buildFlags = [ "vmlinux" ];
installPhase = ''
runHook preInstall
cp vmlinux "$out"
runHook postInstall
'';
};
pigeon = pkgs.writeShellApplication {
name = "pigeon";
runtimeInputs = with pkgs; [ coreutils curl e2fsprogs firecracker git ];
text = builtins.replaceStrings
[ "@kernel@" "@rootfs@" ] [ "${kernel}" "${rootfs}"]
(builtins.readFile ./pigeon.sh);
};
hooks = pkgs.writeText "pigeon-hooks.json"
(builtins.replaceStrings [ "@pigeon@" ] [ "${pigeon}" ]
(builtins.readFile ./hooks.json));
pigeon-webhook = pkgs.writeShellApplication {
name = "pigeon-webhook";
runtimeInputs = with pkgs; [ webhook ];
text = ''
: "''${PIGEON_GITLAB_SECRET:?}"
: "''${PIGEON_GITLAB_TOKEN:?}"
: "''${PIGEON_PROJECTS:?}"
exec webhook -hooks ${hooks} -template -port 3000 -verbose
'';
};
in {
packages.x86_64-linux.default = pigeon-webhook;
};
}
+39
View File
@@ -0,0 +1,39 @@
[
{
"id": "release",
"execute-command": "@pigeon@",
"include-command-output-in-response": false,
"pass-arguments-to-command": [
{ "source": "payload", "name": "project.id" },
{ "source": "payload", "name": "project.namespace" },
{ "source": "payload", "name": "project.name" },
{ "source": "payload", "name": "commit.id" },
{ "source": "payload", "name": "tag" }
],
"trigger-rule": {
"and": [
{
"match": {
"type": "value",
"parameter": { "source": "header", "name": "X-Gitlab-Event" },
"value": "Release Hook"
}
},
{
"match": {
"type": "value",
"parameter": { "source": "payload", "name": "action" },
"value": "create"
}
},
{
"match": {
"type": "value",
"parameter": { "source": "header", "name": "X-Gitlab-Token" },
"value": "{{ getenv `PIGEON_GITLAB_SECRET` | js }}"
}
}
]
}
}
]
Executable
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
export PATH=/bin TMPDIR=/tmp
trap 'sync ; echo 1 >/proc/sys/kernel/sysrq ; echo b >/proc/sysrq-trigger' EXIT
mount -t proc proc /proc
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmp /tmp
mount -t ext4 -r /dev/vdb /root/student
mount -t ext4 -r /dev/vdc /root/project
mount -t ext4 /dev/vdd /root/results
cp -a /root/student/. /tmp/
cp -a --remove-destination /root/project/build/. /tmp/
chmod -R 777 /tmp
cd /tmp
(
ulimit -t 30 -u 256 -n 256 -f 2048
timeout -s 9 30 \
setpriv --reuid=1 --regid=1 --clear-groups --no-new-privs --inh-caps=-all \
make >/root/results/build.log 2>&1
echo $? >/root/results/build.status
)
[ "$(cat /root/results/build.status)" != 0 ] && exit 1
for TEST in /root/project/tests/*.sh ; do
(
ulimit -t 2 -u 256 -n 256 -f 2048
timeout -s 9 2 \
setpriv --reuid=1 --regid=1 --clear-groups --no-new-privs --inh-caps=-all \
"$TEST" >"/root/results/$(basename "$TEST" .sh)" 2>/dev/null
)
done
Executable
+59
View File
@@ -0,0 +1,59 @@
ID="$1"
NAMESPACE="$2"
PROJECT="$3"
COMMIT="$4"
TAG="$5"
: "${PIGEON_GITLAB_TOKEN:?}"
: "${PIGEON_PROJECTS:?}"
DIRECTORY="$(mktemp -d /tmp/pigeon.XXXXXXXXXX)"
trap 'rm -rf "$DIRECTORY"' EXIT
cd "$DIRECTORY"
git -c advice.detachedHead=false clone -q --rev "$COMMIT" --depth 1 \
"https://oauth2:$PIGEON_GITLAB_TOKEN@gitlab.com/$NAMESPACE/$PROJECT.git" student/
rm -rf student/.git
mkdir -p project/tests project/build
cp -a "$PIGEON_PROJECTS/$PROJECT"/tests/*.sh project/tests/
cp -a "$PIGEON_PROJECTS/$PROJECT"/build/. project/build/
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=""
TESTS="$PIGEON_PROJECTS/$PROJECT/tests"
for TEST in "$TESTS"/*.sh ; do
debugfs -R "cat /$(basename "$TEST" .sh)" results.img >scratch 2>/dev/null
if cmp scratch "$TESTS/$(basename "$TEST" .sh).expected" ; then
RESULT="${RESULT}PASS\n"
else
RESULT="${RESULT}FAIL: $(cat "$TESTS/$(basename "$TEST" .sh).hint")\n"
fi
done
curl -s --show-error --out-null --request PUT \
--header "PRIVATE-TOKEN: $PIGEON_GITLAB_TOKEN" \
--header 'Content-Type: application/json' \
--data "{\"description\": \"$RESULT\"}" \
--url "https://gitlab.com/api/v4/projects/$ID/releases/$TAG"