try fix segfault
This commit is contained in:
parent
e74cd875fa
commit
d32b0c3811
3 changed files with 55 additions and 94 deletions
2
main.go
2
main.go
|
|
@ -51,7 +51,7 @@ func normalizeArch(arch string) string {
|
|||
func setup(arch string) *dagger.Container {
|
||||
tarball := "guix-binary-" + guixVersion + "." + arch + "-linux.tar.xz"
|
||||
return dag.Container(dagger.ContainerOpts{Platform: platformFor[arch]}).
|
||||
From("debian:stable-slim").
|
||||
From("debian:bookworm-slim").
|
||||
WithEnvVariable("DEBIAN_FRONTEND", "noninteractive").
|
||||
WithExec([]string{"apt-get", "update"}).
|
||||
WithExec([]string{"apt-get", "install", "-y", "--no-install-recommends",
|
||||
|
|
|
|||
133
patch.sh
133
patch.sh
|
|
@ -47,122 +47,71 @@ if ! grep -q "Good signature" <<< "$VERIFY_OUTPUT"; then
|
|||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/services/shepherd.scm"
|
||||
s = p.read_text()
|
||||
old = ''' ;; Start shepherd.
|
||||
(execl #$(file-append shepherd "/bin/shepherd")
|
||||
"shepherd" "--config"
|
||||
#$(shepherd-configuration-file services shepherd)))))'''
|
||||
new = ''' ;; Start shepherd. Log to the console so boot failures are visible.
|
||||
(execl #$(file-append shepherd "/bin/shepherd")
|
||||
"shepherd" "--config"
|
||||
#$(shepherd-configuration-file services shepherd)
|
||||
"--logfile" "/dev/console"))))'''
|
||||
assert old in s, "shepherd-boot-gexp execl not found"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched shepherd-boot-gexp")
|
||||
PY
|
||||
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').
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/system.scm"
|
||||
s = p.read_text()
|
||||
old = ''' "quiet"))'''
|
||||
new = ''' "verbose"))'''
|
||||
assert s.count(old) == 1, f"expected one 'quiet' kernel arg, found {s.count(old)}"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched %default-kernel-arguments")
|
||||
PY
|
||||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / ".guix-channel"
|
||||
s = p.read_text()
|
||||
old = '''(keyring-reference "keyring")'''
|
||||
new = '''(keyring-reference "patched")'''
|
||||
assert old in s, "keyring-reference not found in .guix-channel"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched .guix-channel keyring-reference")
|
||||
PY
|
||||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/services/networking.scm"
|
||||
s = p.read_text()
|
||||
old = '"--nodnsproxy"'
|
||||
new = '"--nodnsproxy" "--debug"'
|
||||
assert s.count(old) == 1, f"expected one --nodnsproxy, found {s.count(old)}"
|
||||
s = s.replace(old, new, 1)
|
||||
old2 = '#:log-file "/var/log/connman.log"'
|
||||
new2 = '#:log-file "/dev/console"'
|
||||
assert s.count(old2) == 1, f"expected one connman.log, found {s.count(old2)}"
|
||||
s = s.replace(old2, new2, 1)
|
||||
p.write_text(s)
|
||||
print("patched connman to be verbose on the console")
|
||||
PY
|
||||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/installer/final.scm"
|
||||
s = p.read_text()
|
||||
old = '"--fallback"'
|
||||
new = '"--fallback" "--verbosity=3" "--max-silent-time=120"'
|
||||
assert s.count(old) == 1, f"expected one --fallback, found {s.count(old)}"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched installer install command with --verbosity=3 and --max-silent-time=120")
|
||||
PY
|
||||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/installer/final.scm"
|
||||
s = p.read_text()
|
||||
old = ''' (set! ret (run-command install-command #:tty? #t)))'''
|
||||
new = ''' (installer-log-line "debug: final install command: ~s" install-command)
|
||||
(format #t "debug: final install command: ~s~%" install-command)
|
||||
(force-output)
|
||||
(set! ret (run-command install-command #:tty? #f)))'''
|
||||
assert old in s, "final run-command call not found"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched installer final command to log and avoid PTY")
|
||||
PY
|
||||
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.
|
||||
python3 - "$SRC" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
p = Path(sys.argv[1]) / "gnu/installer/utils.scm"
|
||||
s = p.read_text()
|
||||
old = '(list display)'
|
||||
new = '(list display %installer-log-line-hook)'
|
||||
assert s.count(old) == 1, f"expected one (list display), found {s.count(old)}"
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print("patched installer run-command to log output")
|
||||
PY
|
||||
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.
|
||||
|
|
|
|||
14
pull.sh
14
pull.sh
|
|
@ -4,7 +4,19 @@ set -euo pipefail
|
|||
export PATH="/var/guix/profiles/per-user/root/current-guix/bin:${PATH}"
|
||||
|
||||
# Clone + verify + patch + sign the pinned Guix, producing /tmp/channels-local.scm.
|
||||
bash /workspace/patch.sh
|
||||
# patch.sh is idempotent (it re-clones from scratch each time), so retry the
|
||||
# whole thing to ride out transient segfaults in the container's python3.
|
||||
PATCH_OK=0
|
||||
for attempt in $(seq 1 3); do
|
||||
echo "patch: attempt ${attempt}/3"
|
||||
if bash /workspace/patch.sh; then
|
||||
PATCH_OK=1
|
||||
break
|
||||
fi
|
||||
echo "patch.sh failed (exit $?); retrying in 5s..." >&2
|
||||
sleep 5
|
||||
done
|
||||
[ "${PATCH_OK}" = "1" ] || { echo "patch.sh failed after retries" >&2; exit 1; }
|
||||
|
||||
DAEMON_PID=""
|
||||
start_daemon() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue