summaryrefslogtreecommitdiff
path: root/infra/guix/test.sh
diff options
context:
space:
mode:
Diffstat (limited to 'infra/guix/test.sh')
-rw-r--r--infra/guix/test.sh166
1 files changed, 166 insertions, 0 deletions
diff --git a/infra/guix/test.sh b/infra/guix/test.sh
new file mode 100644
index 0000000..4425303
--- /dev/null
+++ b/infra/guix/test.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+# Copyright (C) 2026 Doan Luu Duc Tai <doan@luuduct.ai>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+set -eu
+umask 077
+ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
+for SCRIPT in "$ROOT/infra/guix/"*.sh; do
+ sh -n "$SCRIPT"
+done
+sh "$ROOT/infra/guix/test-bootstrap.sh"
+sh "$ROOT/infra/guix/test-recovery.sh"
+STAGE=$(mktemp -d)
+trap 'find "$STAGE" -type d -exec chmod u+w {} +; rm -rf -- "$STAGE"' EXIT HUP INT TERM
+mkdir -p "$STAGE/source/infra/guix" "$STAGE/source/bin" "$STAGE/fake/bin" "$STAGE/toolbin" \
+ "$STAGE/fake/gnu/store/readonly" "$STAGE/sentinel"
+cp "$ROOT/infra/guix/"*.sh "$STAGE/source/infra/guix/"
+cp "$ROOT/infra/podman.conf" "$STAGE/source/infra/podman.conf"
+cp "$ROOT/infra/guix/containers.conf" "$STAGE/source/infra/guix/containers.conf"
+printf '%s\n' 'ciphertext fixture' > "$STAGE/backup.tar.gz.gpg"
+printf '%s\n' 'channels fixture' > "$STAGE/source/bin/guix-channels.scm"
+printf '%s\n' '#!/bin/sh' 'printf "%s\n" "${CONTAINERS_CONF:-}" > "$0.environment"' 'printf "%s\n" "$@"' > "$STAGE/source/bin/stack"
+chmod 700 "$STAGE/source/bin/stack"
+for TOOL in podman gpg; do
+ printf '%s\n' '#!/bin/sh' 'exit 0' > "$STAGE/fake/bin/$TOOL"
+ chmod 700 "$STAGE/fake/bin/$TOOL"
+done
+printf '%s\n' 'Immutable runtime content.' > "$STAGE/fake/gnu/store/readonly/value"
+printf '%s\n' 'Independent sentinel.' > "$STAGE/sentinel/value"
+ln -s "$STAGE/sentinel" "$STAGE/fake/gnu/store/readonly/outside"
+chmod 555 "$STAGE/sentinel"
+chmod 444 "$STAGE/sentinel/value"
+chmod 555 "$STAGE/fake/gnu/store" "$STAGE/fake/gnu/store/readonly"
+chmod 444 "$STAGE/fake/gnu/store/readonly/value"
+tar -czf "$STAGE/runtime.tar.gz" -C "$STAGE/fake" .
+chmod -R u+w "$STAGE/fake/gnu/store"
+cat > "$STAGE/toolbin/guix" <<'GUIX'
+#!/bin/sh
+set -eu
+case "$1" in
+ describe) printf '%s\n' 'channels fixture' ;;
+ time-machine) printf '%s\n' "$FAKE_PACK" ;;
+ *) exit 1 ;;
+esac
+GUIX
+chmod 700 "$STAGE/toolbin/guix"
+FAKE_PACK="$STAGE/runtime.tar.gz" PATH="$STAGE/toolbin:$PATH" sh "$STAGE/source/infra/guix/build.sh"
+[ -z "$(find "$STAGE/source/bin" -maxdepth 1 -name '.guix-build.*' -print -quit)" ] || {
+ echo 'Build left immutable staging directories.' >&2; exit 1;
+}
+[ "$(stat -c %a "$STAGE/sentinel")" = 555 ]
+[ "$(stat -c %a "$STAGE/sentinel/value")" = 444 ]
+cmp "$STAGE/runtime.tar.gz" "$STAGE/source/bin/guix-runtime.tar.gz"
+mkdir -p "$STAGE/release/stack/bin/chat" "$STAGE/release/stack/infra/guix" "$STAGE/release/stack/var"
+cp "$STAGE/source/bin/guix-runtime.tar.gz" "$STAGE/source/bin/guix-channels.scm" "$STAGE/release/stack/bin/"
+cp "$STAGE/source/infra/guix/"*.sh "$STAGE/release/stack/infra/guix/"
+cp "$ROOT/infra/guix/containers.conf" "$STAGE/release/stack/infra/guix/containers.conf"
+cp "$ROOT/LICENSE" "$STAGE/release/stack/LICENSE"
+printf '%s\n' 'services fixture' > "$STAGE/release/stack/infra/services.toml"
+printf '%s\n' 'image lock fixture' > "$STAGE/release/stack/var/image-lock.json"
+printf '%s\n' 'image export fixture' > "$STAGE/release/stack/var/images.tar"
+for ARTIFACT in server crypto.wasm SOURCE.tar.gz; do
+ printf '%s\n' 'chat artifact fixture' > "$STAGE/release/stack/bin/chat/$ARTIFACT"
+done
+cat > "$STAGE/release/stack/bin/stack" <<'STACK'
+#!/bin/sh
+set -eu
+[ "$#" = 4 ] && [ "$1" = restore ] && [ "$2" = --release ] || exit 1
+mkdir "$4"
+tar -xzf "$3" --strip-components=1 -C "$4"
+STACK
+chmod 700 "$STAGE/release/stack/bin/stack"
+tar -czf "$STAGE/release.tar.gz" -C "$STAGE/release" stack
+sh "$STAGE/source/infra/guix/bundle.sh" "$STAGE/backup.tar.gz.gpg" "$STAGE/release.tar.gz" "$STAGE/recovery.tar.gz"
+if sh "$STAGE/source/infra/guix/bundle.sh" "$STAGE/backup.tar.gz.gpg" "$STAGE/release.tar.gz" "$STAGE/recovery.tar.gz"; then
+ echo 'Overwrote existing archive.' >&2; exit 1
+fi
+tar -xzf "$STAGE/recovery.tar.gz" -C "$STAGE"
+[ -x "$STAGE/stack-recovery/bootstrap" ] || { echo 'Store bootstrap missing from capsule.' >&2; exit 1; }
+cmp "$ROOT/infra/guix/containers.conf" "$STAGE/stack-recovery/containers.conf"
+(cd "$STAGE/stack-recovery" && sha256sum --check --strict SHA256SUMS)
+cmp "$STAGE/backup.tar.gz.gpg" "$STAGE/stack-recovery/backup.tar.gz.gpg"
+cmp "$STAGE/release.tar.gz" "$STAGE/stack-recovery/release.tar.gz"
+cmp "$STAGE/release/stack/bin/stack" "$STAGE/stack-recovery/stack"
+cmp "$STAGE/release/stack/LICENSE" "$STAGE/stack-recovery/LICENSE"
+[ "$(stat -c %a "$STAGE/recovery.tar.gz")" = 600 ]
+if cmp -s "$STAGE/source/bin/stack" "$STAGE/stack-recovery/stack"; then
+ echo 'Bundled source-tree executable instead of release executable.' >&2; exit 1
+fi
+if "$STAGE/stack-recovery/restore" "$STAGE/source"; then
+ echo 'Accepted existing restore destination.' >&2; exit 1
+fi
+cp "$STAGE/source/infra/guix/run.sh" "$STAGE/source/bin/run"
+OUTPUT=$(sh "$STAGE/source/bin/run" status)
+EXPECTED=$(printf '%s\n' --root "$STAGE/source" status)
+[ "$OUTPUT" = "$EXPECTED" ]
+[ "$(cat "$STAGE/source/bin/stack.environment")" = "$STAGE/source/infra/guix/containers.conf" ]
+mv "$STAGE/source/bin/guix-runtime.tar.gz" "$STAGE/saved-runtime.tar.gz"
+OUTPUT=$(env -u CONTAINERS_CONF sh "$STAGE/source/bin/run" status)
+[ "$OUTPUT" = "$EXPECTED" ]
+[ "$(cat "$STAGE/source/bin/stack.environment")" = '' ]
+mv "$STAGE/saved-runtime.tar.gz" "$STAGE/source/bin/guix-runtime.tar.gz"
+find "$STAGE/source/var/guix" -name podman | read -r MATCH
+PUBLISHED=$(sha256sum "$STAGE/source/bin/guix-runtime.tar.gz")
+PUBLISHED=${PUBLISHED%% *}
+DIRECTORY_MODE=$(stat -c %a "$STAGE/source/var/guix/$PUBLISHED/gnu/store/readonly")
+FILE_MODE=$(stat -c %a "$STAGE/source/var/guix/$PUBLISHED/gnu/store/readonly/value")
+chmod -R a-w "$STAGE/fake/gnu/store"
+tar -czf "$STAGE/failed-runtime.tar.gz" -C "$STAGE/fake" bin/podman gnu
+chmod -R u+w "$STAGE/fake/gnu/store"
+cp "$STAGE/failed-runtime.tar.gz" "$STAGE/source/bin/guix-runtime.tar.gz"
+if sh "$STAGE/source/bin/run" status; then
+ echo 'Accepted runtime missing GnuPG.' >&2; exit 1;
+fi
+[ -z "$(find "$STAGE/source/var/guix" -maxdepth 1 -name '.unpack.*' -print -quit)" ] || {
+ echo 'Failed launcher left immutable staging directories.' >&2; exit 1;
+}
+[ -x "$STAGE/source/var/guix/$PUBLISHED/bin/gpg" ]
+[ "$(stat -c %a "$STAGE/source/var/guix/$PUBLISHED/gnu/store/readonly")" = "$DIRECTORY_MODE" ]
+[ "$(stat -c %a "$STAGE/source/var/guix/$PUBLISHED/gnu/store/readonly/value")" = "$FILE_MODE" ]
+[ "$(stat -c %a "$STAGE/sentinel")" = 555 ]
+[ "$(stat -c %a "$STAGE/sentinel/value")" = 444 ]
+cmp "$STAGE/runtime.tar.gz" "$STAGE/stack-recovery/runtime.tar.gz"
+cp "$STAGE/runtime.tar.gz" "$STAGE/source/bin/guix-runtime.tar.gz"
+printf 'tamper' >> "$STAGE/stack-recovery/backup.tar.gz.gpg"
+if (cd "$STAGE/stack-recovery" && sha256sum --check --strict SHA256SUMS); then
+ echo 'Accepted changed ciphertext.' >&2; exit 1
+fi
+rm "$STAGE/release/stack/bin/guix-channels.scm"
+tar -czf "$STAGE/missing.tar.gz" -C "$STAGE/release" stack
+if sh "$STAGE/source/infra/guix/bundle.sh" "$STAGE/backup.tar.gz.gpg" "$STAGE/missing.tar.gz" "$STAGE/other.tar.gz"; then
+ echo 'Accepted missing provenance.' >&2; exit 1
+fi
+cp "$STAGE/source/bin/guix-channels.scm" "$STAGE/release/stack/bin/guix-channels.scm"
+for ARTIFACT in image-lock.json images.tar; do
+ mv "$STAGE/release/stack/var/$ARTIFACT" "$STAGE/$ARTIFACT"
+ tar -czf "$STAGE/missing-image.tar.gz" -C "$STAGE/release" stack
+ if sh "$STAGE/source/infra/guix/bundle.sh" "$STAGE/backup.tar.gz.gpg" "$STAGE/missing-image.tar.gz" "$STAGE/missing-image-output.tar.gz"; then
+ echo 'Accepted release without complete saved images.' >&2; exit 1
+ fi
+ [ ! -e "$STAGE/missing-image-output.tar.gz" ]
+ mv "$STAGE/$ARTIFACT" "$STAGE/release/stack/var/$ARTIFACT"
+done
+for PRIVATE in .env var/data; do
+ mkdir -p "$(dirname "$STAGE/release/stack/$PRIVATE")"
+ printf '%s\n' 'private fixture' > "$STAGE/release/stack/$PRIVATE"
+ tar -czf "$STAGE/private.tar.gz" -C "$STAGE/release" stack
+ if sh "$STAGE/source/infra/guix/bundle.sh" "$STAGE/backup.tar.gz.gpg" "$STAGE/private.tar.gz" "$STAGE/private-output.tar.gz"; then
+ echo 'Accepted private release contents.' >&2; exit 1
+ fi
+ [ ! -e "$STAGE/private-output.tar.gz" ]
+ rm "$STAGE/release/stack/$PRIVATE"
+done
+printf '%s\n' 'Bundle, immutable cleanup, integrity, overwrite, launcher, and missing-artifact checks passed.'