diff options
| author | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-11-01 07:13:45 -0600 |
|---|---|---|
| committer | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-11-01 07:13:45 -0600 |
| commit | 9349a2de648e241da8e00ba7ae25cb2a877ff3e9 (patch) | |
| tree | 2fea913ee0aaff4370877f4a91c911db2fe37592 /infra/guix/bootstrap.sh | |
| parent | 5b89c370d5974f057306a52089f14c337565fd1d (diff) | |
feat(guix): carry prebuilt runtime and recover preserved state
Diffstat (limited to 'infra/guix/bootstrap.sh')
| -rw-r--r-- | infra/guix/bootstrap.sh | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/infra/guix/bootstrap.sh b/infra/guix/bootstrap.sh new file mode 100644 index 0000000..6f53dbc --- /dev/null +++ b/infra/guix/bootstrap.sh @@ -0,0 +1,86 @@ +#!/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 +fail() { printf '%s\n' "$1" >&2; exit 1; } +safe_directory() { + [ -d "$1" ] && [ ! -L "$1" ] && [ "$(stat -c %u "$1")" = 0 ] && + [ -z "$(find "$1" -maxdepth 0 -perm /022 -print)" ] +} +[ "$#" = 0 ] || fail 'Usage: sudo ./stack-recovery/bootstrap' +[ "$(id -u)" = 0 ] || fail 'Run bootstrap explicitly as root; restore runs as the service user.' +[ "$(uname -sm)" = 'Linux x86_64' ] || fail 'Requires x86_64 Linux.' +command -v flock >/dev/null || fail 'Host util-linux flock is required.' +command -v diff >/dev/null || fail 'Host GNU diffutils is required.' +BUNDLE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +(cd "$BUNDLE" && sha256sum --check --strict SHA256SUMS) +[ ! -e /var/guix ] && [ ! -L /var/guix ] || fail 'Managed Guix installation found; refusing bootstrap.' +[ ! -L /gnu ] && [ ! -L /gnu/store ] || fail 'Symlink /gnu or store; refusing bootstrap.' +STAGE= +INITIAL= +trap '[ -z "$STAGE" ] || rm -rf -- "$STAGE"; [ -z "$INITIAL" ] || rm -rf -- "$INITIAL"' EXIT HUP INT TERM +if [ ! -e /gnu ]; then + INITIAL=$(mktemp -d /.stack-guix.XXXXXX) + chmod 755 "$INITIAL" + mkdir -m 755 "$INITIAL/store" "$INITIAL/.stack-runtimes" + mv -T --no-clobber -- "$INITIAL" /gnu + rm -rf -- "$INITIAL" + INITIAL= +fi +safe_directory /gnu && safe_directory /gnu/.stack-runtimes || fail 'Foreign or writable /gnu ownership metadata; refusing bootstrap.' +exec 9>/gnu/.stack-runtimes/install.lock +flock 9 +if [ -e /gnu/store ]; then + safe_directory /gnu/store || fail 'Foreign or writable Guix store; refusing bootstrap.' +else + [ ! -L /gnu/store ] || fail 'Symlink store; refusing bootstrap.' + mkdir -m 755 /gnu/store +fi +DIGEST=$(sha256sum "$BUNDLE/runtime.tar.gz") +DIGEST=${DIGEST%% *} +MARKER=/gnu/.stack-runtimes/$DIGEST +if [ -e "$MARKER" ] || [ -L "$MARKER" ]; then + [ -f "$MARKER" ] && [ ! -L "$MARKER" ] && [ "$(stat -c %u "$MARKER")" = 0 ] && + [ -z "$(find "$MARKER" -perm /222 -print)" ] && [ "$(cat "$MARKER")" = "$DIGEST" ] || + fail 'Invalid completed-runtime marker; refusing bootstrap.' + printf '%s\n' 'Guix runtime already installed.' + exit 0 +fi +STAGE=$(mktemp -d /gnu/.stack-stage.XXXXXX) +tar -xzf "$BUNDLE/runtime.tar.gz" --no-same-owner -C "$STAGE" +[ -d "$STAGE/gnu" ] && [ ! -L "$STAGE/gnu" ] && + [ -d "$STAGE/gnu/store" ] && [ ! -L "$STAGE/gnu/store" ] || fail 'Runtime has no canonical Guix store.' +set -- "$STAGE/gnu/store/"* +[ -e "$1" ] || [ -L "$1" ] || fail 'Runtime Guix store is empty.' +chown -hR 0:0 "$STAGE/gnu/store" +chmod -R a-w "$STAGE/gnu/store" +for ENTRY do + TARGET=/gnu/store/${ENTRY##*/} + if [ -e "$TARGET" ] || [ -L "$TARGET" ]; then + diff -qr --no-dereference -- "$ENTRY" "$TARGET" || fail 'Conflicting Guix store entry; existing data retained.' + [ -z "$(find "$TARGET" \( ! -user 0 -o \( ! -type l -perm /222 \) \) -print -quit)" ] || + fail 'Existing Guix store entry has unsafe ownership or write permissions.' + else + mv -T --no-clobber -- "$ENTRY" "$TARGET" + [ ! -e "$ENTRY" ] && [ ! -L "$ENTRY" ] || fail 'Guix store entry appeared during installation; retry bootstrap.' + fi +done +printf '%s\n' "$DIGEST" > "$STAGE/complete" +chmod 444 "$STAGE/complete" +mv -T --no-clobber -- "$STAGE/complete" "$MARKER" +[ ! -e "$STAGE/complete" ] || fail 'Runtime marker appeared during installation; retry bootstrap.' +printf '%s\n' 'Guix runtime installed. Run restore as the service user.' |
