145 lines
6.4 KiB
Bash
145 lines
6.4 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Clone the pinned Guix commit, verify its authenticity, apply local debugging
|
|
# patches, sign the result with our own key, and write a channels file that
|
|
# Guix will accept (the channel is named 'guix' with a valid introduction).
|
|
# Called from pull.sh before 'guix pull'.
|
|
|
|
SRC=/tmp/guix-src
|
|
CHANNELS=/workspace/channels.scm
|
|
FINGERPRINT="E17D867D37C0603143EFE9437EFF749EC5D6018C"
|
|
FINGERPRINT_SPACED="E17D 867D 37C0 6031 43EF E943 7EFF 749E C5D6 018C"
|
|
|
|
# Pinned Guix commit from channels.scm.
|
|
COMMIT="$(grep -oE '\(commit "[0-9a-f]{40}"\)' "$CHANNELS" | grep -oE '[0-9a-f]{40}')"
|
|
[ -n "$COMMIT" ] || { echo "error: no pinned commit in channels.scm" >&2; exit 1; }
|
|
echo "pinned Guix commit: $COMMIT"
|
|
|
|
# Shallow-clone the pinned commit. The keyring branch is fetched only as a
|
|
# remote-tracking ref (for the local integrity check below), NOT as a local
|
|
# branch, so that guix pull's own clone of this repo only sees the 'patched'
|
|
# branch and never trips over the keyring tip.
|
|
rm -rf "$SRC"
|
|
git init -q "$SRC"
|
|
git -C "$SRC" remote add origin https://codeberg.org/guix/guix.git
|
|
git -C "$SRC" fetch -q --depth 1 origin "$COMMIT"
|
|
git -C "$SRC" fetch -q --depth 1 origin refs/heads/keyring:refs/remotes/origin/keyring
|
|
git -C "$SRC" checkout -q "$COMMIT"
|
|
|
|
# Import the official signing keys and verify the pinned commit's signature,
|
|
# so we patch a known-authentic base.
|
|
export GNUPGHOME=/tmp/gnupg
|
|
rm -rf "$GNUPGHOME"
|
|
mkdir -p "$GNUPGHOME"
|
|
chmod 700 "$GNUPGHOME"
|
|
echo "trust-model always" > "$GNUPGHOME/gpg.conf"
|
|
mkdir -p /tmp/keyring
|
|
git -C "$SRC" archive origin/keyring | tar -x -C /tmp/keyring
|
|
for k in /tmp/keyring/*.key; do
|
|
gpg --batch --import "$k" >/dev/null 2>&1 || true
|
|
done
|
|
VERIFY_OUTPUT="$(git -C "$SRC" verify-commit "$COMMIT" 2>&1 || true)"
|
|
echo "$VERIFY_OUTPUT"
|
|
if ! grep -q "Good signature" <<< "$VERIFY_OUTPUT"; then
|
|
echo "error: pinned commit $COMMIT failed signature verification" >&2
|
|
exit 1
|
|
fi
|
|
echo "verified pinned commit signature"
|
|
|
|
# Replace a fixed string that occurs on exactly one line, aborting if the count
|
|
# is not 1 (the assert mirrors the old python3 patches, without depending on a
|
|
# Python interpreter that turned out to segfault in the container). $needle is
|
|
# matched literally by grep; $sed_expr is the equivalent GNU sed substitution.
|
|
replace_once() {
|
|
local file="$1" needle="$2" sed_expr="$3" n
|
|
n="$(grep -Fc -- "$needle" "$file")"
|
|
if [ "$n" != "1" ]; then
|
|
echo "error: expected one '$needle' in $file, found $n" >&2
|
|
return 1
|
|
fi
|
|
sed -i "$sed_expr" "$file"
|
|
}
|
|
|
|
# Route shepherd's log to the console, so boot failures are visible on screen
|
|
# instead of only in /var/log/messages.
|
|
replace_once "$SRC/gnu/services/shepherd.scm" \
|
|
' ;; Start shepherd.' \
|
|
's| ;; Start shepherd\.| ;; Start shepherd. Log to the console so boot failures are visible.|'
|
|
replace_once "$SRC/gnu/services/shepherd.scm" \
|
|
'#$(shepherd-configuration-file services shepherd)))))' \
|
|
's|#\$(shepherd-configuration-file services shepherd)))))|#\$(shepherd-configuration-file services shepherd)\n "--logfile" "/dev/console"))))|'
|
|
|
|
# Make the kernel verbose by default (drop 'quiet').
|
|
replace_once "$SRC/gnu/system.scm" \
|
|
' "quiet"))' \
|
|
's| "quiet"))| "verbose"))|'
|
|
|
|
# Point the channel's keyring-reference at the main branch. The keyring is
|
|
# loaded (unconditionally) but never used for verification, because
|
|
# first-signed-commit equals the channel commit (an empty commit range). So an
|
|
# empty keyring off the 'patched' branch is fine and avoids needing a separate
|
|
# keyring branch that libgit2 refuses to fetch.
|
|
replace_once "$SRC/.guix-channel" \
|
|
' (keyring-reference "keyring")' \
|
|
's| (keyring-reference "keyring")| (keyring-reference "patched")|'
|
|
|
|
# Make connman verbose: run it with --debug and send its output to the console
|
|
# instead of /var/log/connman.log, so a networking hang explains itself.
|
|
replace_once "$SRC/gnu/services/networking.scm" \
|
|
'"--nodnsproxy"' \
|
|
's|"--nodnsproxy"|"--nodnsproxy" "--debug"|'
|
|
replace_once "$SRC/gnu/services/networking.scm" \
|
|
'#:log-file "/var/log/connman.log"' \
|
|
's|#:log-file "/var/log/connman\.log"|#:log-file "/dev/console"|'
|
|
|
|
# Make the installer's `guix system init` verbose so its progress (substitute
|
|
# downloads, builds) is visible instead of a silent hang.
|
|
replace_once "$SRC/gnu/installer/final.scm" \
|
|
'"--fallback"' \
|
|
's|"--fallback"|"--fallback" "--verbosity=3" "--max-silent-time=120"|'
|
|
|
|
# The installer normally runs the final `guix system init` in a PTY. That is
|
|
# nicer for progress bars, but it makes this failure mode harder to diagnose
|
|
# when the newt screen goes blank. For this debug image, use the plain pipe
|
|
# path and print the exact command right before it runs.
|
|
replace_once "$SRC/gnu/installer/final.scm" \
|
|
'(set! ret (run-command install-command #:tty? #t)))' \
|
|
's|(set! ret (run-command install-command #:tty? #t)))|(installer-log-line "debug: final install command: ~s" install-command)\n (format #t "debug: final install command: ~s~%" install-command)\n (force-output)\n (set! ret (run-command install-command #:tty? #f)))|'
|
|
|
|
# Log every line the installer's commands print to the installer log, so the
|
|
# full install output is captured and visible.
|
|
replace_once "$SRC/gnu/installer/utils.scm" \
|
|
'(list display)' \
|
|
's|(list display)|(list display %installer-log-line-hook)|'
|
|
|
|
# Sign the patched tree as an orphan commit (no parent), so that guix pull's
|
|
# clone never tries to fetch the pinned commit's absent parent.
|
|
gpg --batch --import /workspace/keys/private.key >/dev/null 2>&1
|
|
git -C "$SRC" checkout -q --orphan patched
|
|
git -C "$SRC" add -A
|
|
git -C "$SRC" \
|
|
-c user.email=debug@guix-iso.invalid \
|
|
-c user.name="guix-iso debug" \
|
|
-c user.signingkey="$FINGERPRINT" \
|
|
commit -q -S -m "debug: console shepherd log + verbose kernel"
|
|
# Drop the keyring ref (only needed for the integrity check above) and the
|
|
# shallow marker, so guix pull's clone only sees the self-contained orphan
|
|
# commit.
|
|
git -C "$SRC" update-ref -d refs/remotes/origin/keyring
|
|
rm -f "$SRC/.git/shallow"
|
|
NEW_COMMIT="$(git -C "$SRC" rev-parse HEAD)"
|
|
echo "patched commit: $NEW_COMMIT"
|
|
|
|
cat > /tmp/channels-local.scm <<EOF
|
|
(list (channel
|
|
(name 'guix)
|
|
(url "file://$SRC")
|
|
(commit "$NEW_COMMIT")
|
|
(introduction
|
|
(make-channel-introduction
|
|
"$NEW_COMMIT"
|
|
(openpgp-fingerprint
|
|
"$FINGERPRINT_SPACED")))))
|
|
EOF
|
|
echo "wrote /tmp/channels-local.scm"
|