diff options
Diffstat (limited to 'infra/guix/test.sh')
| -rw-r--r-- | infra/guix/test.sh | 166 |
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.' |
