feat: port the missing KDE Plasma dependency chain and harden the auth stack
redbear-ci / check (push) Has been cancelled
redbear-ci / check (push) Has been cancelled
Unblocks plasma-workspace/plasma-desktop, which required 14 packages that had
no in-tree recipe. Sources are reproducible from each recipe's tar= + blake3;
the vendored source/ trees are deliberately not committed here (212M).
New recipes:
KF6 6.28.0 kf6-kholidays, kf6-krunner, kf6-kstatusnotifieritem,
kf6-kunitconversion
Plasma 6.7.2 knighttime, layer-shell-qt, libkscreen, libksysguard,
plasma-activities-stats, plasma5support, kscreenlocker
Qt 6.11.1 qtpositioning, qtspeech, qttools
libksysguard carries P0-redox-process-backend.patch: processes_local_p.cpp
dispatches on platform macros and had no __redox__ arm, so ProcessesLocal was
entirely undefined. Adds a real backend reading /scheme/proc/ps (pid, ppid,
real+effective ids, thread count, state) and /scheme/sys/mem, with kill() for
signals. Upstream's generic fallback is a pure stub and was not used. Absent
facilities (no setpriority/sched_setscheduler/ioprio on a microkernel) report
NotSupported rather than pretending.
Restored, no longer disabled:
- night colour: kcms/nighttime + kwin's nightlight plugin, now that
KNightTime/Qt6Positioning/KF6Holidays exist
- KIO FileWidgets (file dialog, places model), wrongly swept in with the
unportable kiod/kssld/kioworkers subdirs
- kf6-ktexteditor text-to-speech: removes a disguised stub that rewrote
speechEngine() to return nullptr and mangled call sites into invalid C++
- kwin KWIN_BUILD_SCREENLOCKER=ON (needs kscreenlocker; see below)
Toolchain and recipe fixes:
- redox-toolchain.cmake: append -lgcc. __extendhfsf2/__extendbfsf2 are
global in libgcc.a but hidden in libgcc_s.so (the Redox libgcc version map
stops at GCC_7.0.0 and never emits the GCC_12/13 nodes upstream exports),
and GCC omits -lgcc for -shared, so no C++23 shared library using
std::format<_Float16|bfloat16_t> could link.
- qtmultimedia: rewrite /usr/src/.../meta_types paths and stage metatypes,
so consumers using qt_internal_add_qml_module can configure.
- qtspeech: qtmultimedia is mandatory, not optional -- upstream return()s
with only a NOTICE without it, yielding a green build over an empty
package. Asserts its own output so that cannot recur.
- narrow Linux-only guards with AND NOT REDOX where the toolchain sets
CMAKE_SYSTEM_NAME=Linux purely to get UNIX=TRUE (kio LibMount,
libksysguard NL/Sensors, plasma-workspace NetworkManagerQt).
- drop REQUIRED components that are declared but referenced nowhere
(Location, QCoro6, Qt6 Test) -- verified by exhaustive grep.
- DESTDIR installs where KDE emits absolute KDE_INSTALL_FULL_* paths that
--prefix cannot re-root, which otherwise write into the build host.
Auth stack:
- pam-redbear: add PAM_MODULE_UNKNOWN (28), missing from both the header and
lib.rs; realign 30/31 to Linux-PAM's PAM_CONV_AGAIN/PAM_INCOMPLETE, which
previously held Red Bear-only names on standard values, so anything built
against stock PAM headers mis-decoded them.
- redbear-authd: support yescrypt ($y$ -- the default shadow format on
current Debian/Ubuntu/Fedora, previously an unexplained login failure),
bcrypt and md5-crypt. Plaintext shadow entries now require the
/etc/redbear/allow-plaintext-passwords sentinel and warn on every use,
instead of being compared silently. 19/19 host tests pass.
Build-system hardening:
- verify-tracked-sources.sh, wired into preflight: fails on deletion of any
tracked vendored source, and on modifications with no baseline entry.
local/sources/ had a dirty gate and version checks; local/recipes/*/source/
had none, which allowed an rm -rf to delete 7958 tracked files and a
pristine re-extract to silently overwrite committed fixes (kwin's
std::expected/vulkan-hpp and X11 gating, kio's Q_OS_REDOX resolver guards).
- validate-source-trees.py: resolve recipe.toml through the overlay symlink;
it reported false MISSING for symlinked recipes.
- test-sddm-virgl-qemu.sh: the null-proxy check used report(), which PASSES
on regex match -- so the known Qt6 Wayland null+8 fault would report PASS
when present. Added report_absent().
kscreenlocker builds only its cmake-level Wayland-only changes so far; the
C++ X11 removal (61 call sites) is not yet applied, so kwin's screenlocker
flag is blocked on it.
This commit is contained in:
@@ -63,6 +63,26 @@ if [ -x "$SCRIPT_DIR/verify-overlay-integrity.sh" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Tracked vendored source integrity.
|
||||
#
|
||||
# local/sources/ (the submodule forks) is protected by two existing gates: the
|
||||
# dirty-source gate in build-redbear.sh and verify-fork-versions.sh. The
|
||||
# vendored recipe trees under local/recipes/*/source/ carry equally durable
|
||||
# committed work and had NO equivalent gate, which allowed two real incidents:
|
||||
# an `rm -rf <recipe>/source` deleting thousands of tracked files, and a
|
||||
# pristine tarball re-extract silently overwriting committed Red Bear fixes
|
||||
# (kwin's std::expected/vulkan-hpp + X11 gating, kio's Q_OS_REDOX resolver
|
||||
# guards) with no error raised. This closes that asymmetry.
|
||||
if [ -x "$SCRIPT_DIR/verify-tracked-sources.sh" ] && [ "${REDBEAR_SKIP_TRACKED_SOURCE_CHECK:-0}" != "1" ]; then
|
||||
if ! "$SCRIPT_DIR/verify-tracked-sources.sh"; then
|
||||
echo "" >&2
|
||||
echo ">>> ERROR: tracked vendored source trees are damaged (see above)." >&2
|
||||
echo " Set REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1 to bypass (DANGEROUS: you are" >&2
|
||||
echo " choosing to build from, and potentially commit, a corrupted source tree)." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enforce the "no fake version label" rule (local/AGENTS.md):
|
||||
# every Cat 2 fork's `<X.Y.Z>-rb<B.B.B>` version must match the source
|
||||
# content from upstream `<X.Y.Z>` + Red Bear patches, and the `-rb<B.B.B>`
|
||||
|
||||
@@ -141,6 +141,24 @@ report() { # name regex required
|
||||
fi
|
||||
}
|
||||
|
||||
# Negative assertion: the marker's ABSENCE is the pass condition. These must NOT
|
||||
# go through report(), which passes when the regex MATCHES — using it for a fault
|
||||
# signature inverts the verdict, so an observed fault prints "PASS". That is how
|
||||
# the Qt6 Wayland null+8 fault (the known #1 desktop blocker) could be present in
|
||||
# the serial log and still be reported as a clean run.
|
||||
report_absent() { # name regex severity
|
||||
local name="$1" re="$2" severity="$3"
|
||||
if grep -qiE "$re" "$log" 2>/dev/null; then
|
||||
echo " FAIL $name"
|
||||
grep -niE "$re" "$log" | head -3 | sed 's/^/ /'
|
||||
if [[ "$severity" == "required" ]]; then
|
||||
rc=1
|
||||
fi
|
||||
else
|
||||
echo " PASS $name"
|
||||
fi
|
||||
}
|
||||
|
||||
echo ">>> Boot markers:"
|
||||
report "kernel reached userspace" 'RedoxFS|redox_init|init: |bootstrap' required
|
||||
report "driver-manager started" 'driver-manager' required
|
||||
@@ -151,7 +169,7 @@ report "seatd" 'seatd'
|
||||
report "compositor started" 'redbear-compositor|wayland-0|WAYLAND_DISPLAY' required
|
||||
report "SDDM started" 'sddm' required
|
||||
report "SDDM greeter process" 'sddm-greeter|greeter' required
|
||||
report "no Wayland null-proxy fault" '(wl_proxy_add_listener.*null|fault.*0x8)' optional
|
||||
report_absent "no Wayland null-proxy fault" 'wl_proxy_add_listener.*null|fault.*0x8' required
|
||||
|
||||
echo ">>> Failure signatures:"
|
||||
for sig in 'panic' 'PANIC' 'SIGSEGV' 'page fault' 'Segmentation fault' 'FATAL'; do
|
||||
|
||||
@@ -19,7 +19,13 @@ def build_lookup():
|
||||
continue
|
||||
package_name = recipe_toml.parent.name
|
||||
if package_name not in lookup:
|
||||
lookup[package_name] = recipe_toml.parent
|
||||
# Resolve through the overlay symlink. `recipes/<cat>/<name>` is
|
||||
# frequently a symlink (whole-dir) or contains a symlinked
|
||||
# recipe.toml pointing into `local/recipes/<cat>/<name>`, which is
|
||||
# where the vendored `source/` tree actually lives. Recording the
|
||||
# unresolved parent made the source lookup miss it and report a
|
||||
# false MISSING (e.g. xwayland).
|
||||
lookup[package_name] = recipe_toml.resolve().parent
|
||||
return lookup
|
||||
|
||||
|
||||
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env bash
|
||||
# verify-tracked-sources.sh — integrity gate for git-tracked vendored source trees.
|
||||
#
|
||||
# WHY THIS EXISTS
|
||||
# ---------------
|
||||
# Several recipes under local/recipes/ are "vendored forks": their source/ tree
|
||||
# is committed to git and carries real Red Bear work baked directly into files
|
||||
# that also exist upstream (see local/docs/VERSIONING.md, "baked shim"). That
|
||||
# tree is durable state, but nothing guarded it, and two failure modes have
|
||||
# actually happened:
|
||||
#
|
||||
# 1. DELETION. An ad-hoc `rm -rf <recipe>/source` (to force a clean
|
||||
# re-extract) removes thousands of tracked files. AGENTS.md already warns
|
||||
# that this "has wiped tracked local/recipes/*/source/ trees in the past".
|
||||
#
|
||||
# 2. SILENT OVERWRITE. For a `tar` recipe, deleting source/ makes the
|
||||
# cookbook re-extract PRISTINE upstream over it. Files that exist upstream
|
||||
# get their committed Red Bear edits replaced with upstream content. No
|
||||
# error is raised -- the package may even still build -- so the loss ships
|
||||
# as a silent regression. This is how kwin lost its std::expected /
|
||||
# vulkan-hpp and X11-gating fixes, and kio lost the Q_OS_REDOX guards in
|
||||
# hostinfo.cpp.
|
||||
#
|
||||
# Neither mode is detectable by the existing gates: verify-fork-versions.sh
|
||||
# covers local/sources/ (the submodule forks), and the build-redbear.sh
|
||||
# dirty-source gate likewise only looks at local/sources/. The vendored recipe
|
||||
# trees carry equally durable work and had no equivalent check.
|
||||
#
|
||||
# WHAT IT CHECKS
|
||||
# --------------
|
||||
# Recipe seds legitimately rewrite tracked source files on every build (that is
|
||||
# the current in-tree build model), so "modified" alone cannot mean "damaged".
|
||||
# The gate therefore separates three cases:
|
||||
#
|
||||
# DELETED tracked file under */source/ -> ERROR, always. A build never
|
||||
# legitimately deletes tracked
|
||||
# source.
|
||||
# MODIFIED, not in the baseline -> ERROR. Something changed a
|
||||
# tracked source file that no
|
||||
# recorded recipe sed accounts
|
||||
# for -- the overwrite signature.
|
||||
# MODIFIED, listed in the baseline -> known churn. Reported as a
|
||||
# durability WARNING (this is
|
||||
# uncommitted work living in a
|
||||
# durable tree); escalate to an
|
||||
# error with
|
||||
# REDBEAR_STRICT_TRACKED_SOURCES=1.
|
||||
#
|
||||
# The baseline (local/tracked-source-baseline.txt) is itself tracked, so the set
|
||||
# of "expected to be dirty" files is reviewable in git rather than invisible.
|
||||
#
|
||||
# USAGE
|
||||
# verify-tracked-sources.sh [--quiet] # check (preflight mode)
|
||||
# verify-tracked-sources.sh --update # regenerate the baseline
|
||||
# verify-tracked-sources.sh --list # show current dirty tracked sources
|
||||
#
|
||||
# Bypass: REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1 (emergency only).
|
||||
# Escalate baseline churn to an error: REDBEAR_STRICT_TRACKED_SOURCES=1.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
BASELINE="$ROOT/local/tracked-source-baseline.txt"
|
||||
|
||||
QUIET=0
|
||||
MODE=check
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--quiet) QUIET=1 ;;
|
||||
--update) MODE=update ;;
|
||||
--list) MODE=list ;;
|
||||
-h|--help)
|
||||
sed -n '2,60p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
||||
exit 0 ;;
|
||||
*) echo "verify-tracked-sources.sh: unknown argument '$arg'" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() { [ "$QUIET" -eq 1 ] || echo "$@"; }
|
||||
|
||||
cd "$ROOT" || exit 2
|
||||
|
||||
# Collect tracked files under any local/recipes/**/source/ that git reports as
|
||||
# changed. -z keeps paths with spaces intact (several KDE avatar assets have
|
||||
# them, which is exactly where a naive `xargs` split goes wrong).
|
||||
collect() { # $1 = status filter letter (M or D)
|
||||
local want="$1"
|
||||
git status --porcelain -z -- local/recipes 2>/dev/null \
|
||||
| while IFS= read -r -d '' entry; do
|
||||
local st="${entry:0:2}" path="${entry:3}"
|
||||
case "$st" in
|
||||
*"$want"*) [[ "$path" == */source/* ]] && printf '%s\n' "$path" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
mapfile -t DELETED < <(collect D | LC_ALL=C sort)
|
||||
mapfile -t MODIFIED < <(collect M | LC_ALL=C sort)
|
||||
|
||||
if [ "$MODE" = list ]; then
|
||||
printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$MODE" = update ]; then
|
||||
{
|
||||
echo "# tracked-source-baseline.txt"
|
||||
echo "#"
|
||||
echo "# Tracked files under local/recipes/*/source/ that are EXPECTED to differ"
|
||||
echo "# from HEAD during a normal build -- almost all of it recipe-sed churn."
|
||||
echo "# Consumed by local/scripts/verify-tracked-sources.sh."
|
||||
echo "#"
|
||||
echo "# A file listed here is uncommitted work sitting in a DURABLE tree. That is"
|
||||
echo "# tolerated, not endorsed: commit it, or convert the change into a recipe sed"
|
||||
echo "# or a patch, so a clean re-extract cannot lose it."
|
||||
echo "#"
|
||||
echo "# A line prefixed 'D ' records an INTENTIONAL deletion of a tracked source"
|
||||
echo "# file (e.g. removing a vendored stub). Without an entry, any deletion is"
|
||||
echo "# treated as damage."
|
||||
echo "#"
|
||||
echo "# Regenerate with: ./local/scripts/verify-tracked-sources.sh --update"
|
||||
printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d'
|
||||
printf 'D %s\n' "${DELETED[@]}" | sed '/^D $/d'
|
||||
} > "$BASELINE"
|
||||
echo ">>> baseline updated: $(printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d' | wc -l) modified," \
|
||||
"$(printf '%s\n' "${DELETED[@]}" | sed '/^$/d' | wc -l) deleted -> $BASELINE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---- check mode ----------------------------------------------------------
|
||||
rc=0
|
||||
|
||||
if [ "${REDBEAR_SKIP_TRACKED_SOURCE_CHECK:-0}" = "1" ]; then
|
||||
echo ">>> WARNING: tracked-source integrity check bypassed via REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Load the baseline first: it records both expected modifications (plain lines)
|
||||
# and intentional deletions ("D <path>").
|
||||
declare -A known=()
|
||||
declare -A known_del=()
|
||||
if [ -f "$BASELINE" ]; then
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "$line" == \#* ]] && continue
|
||||
if [[ "$line" == "D "* ]]; then
|
||||
known_del["${line:2}"]=1
|
||||
else
|
||||
known["$line"]=1
|
||||
fi
|
||||
done < "$BASELINE"
|
||||
fi
|
||||
|
||||
# 1. Deletions are damage unless explicitly recorded as intentional.
|
||||
unexpected_del=()
|
||||
for f in "${DELETED[@]}"; do
|
||||
[ -z "$f" ] && continue
|
||||
[ -n "${known_del[$f]:-}" ] || unexpected_del+=("$f")
|
||||
done
|
||||
DELETED=("${unexpected_del[@]}")
|
||||
|
||||
if [ "${#DELETED[@]}" -gt 0 ] && [ -n "${DELETED[0]:-}" ]; then
|
||||
echo "" >&2
|
||||
echo ">>> ERROR: ${#DELETED[@]} git-tracked file(s) under a vendored source/ tree are DELETED." >&2
|
||||
echo " A build never legitimately deletes tracked source. This is the signature of an" >&2
|
||||
echo " ad-hoc 'rm -rf <recipe>/source'." >&2
|
||||
printf ' %s\n' "${DELETED[@]:0:15}" >&2
|
||||
[ "${#DELETED[@]}" -gt 15 ] && echo " ... and $(( ${#DELETED[@]} - 15 )) more" >&2
|
||||
echo "" >&2
|
||||
echo " Restore with: git checkout -- local/recipes/<recipe>/source" >&2
|
||||
echo " To force a clean re-extract of a tracked tree, restore it from git afterwards;" >&2
|
||||
echo " never delete it." >&2
|
||||
rc=1
|
||||
fi
|
||||
|
||||
# 2. Modifications the baseline does not account for.
|
||||
unexpected=()
|
||||
for f in "${MODIFIED[@]}"; do
|
||||
[ -z "$f" ] && continue
|
||||
[ -n "${known[$f]:-}" ] || unexpected+=("$f")
|
||||
done
|
||||
|
||||
if [ "${#unexpected[@]}" -gt 0 ]; then
|
||||
echo "" >&2
|
||||
echo ">>> ERROR: ${#unexpected[@]} tracked source file(s) changed with no baseline entry." >&2
|
||||
echo " Either a recipe gained a new sed, or committed Red Bear work was overwritten" >&2
|
||||
echo " (e.g. a pristine tarball re-extract replacing baked-in fixes)." >&2
|
||||
printf ' %s\n' "${unexpected[@]:0:15}" >&2
|
||||
[ "${#unexpected[@]}" -gt 15 ] && echo " ... and $(( ${#unexpected[@]} - 15 )) more" >&2
|
||||
echo "" >&2
|
||||
echo " Inspect: git diff -- <path>" >&2
|
||||
echo " If the change is LOST WORK: git checkout -- <path>" >&2
|
||||
echo " If the change is EXPECTED: ./local/scripts/verify-tracked-sources.sh --update" >&2
|
||||
rc=1
|
||||
fi
|
||||
|
||||
# 3. Baseline churn: uncommitted work in a durable tree. Visible, not fatal by
|
||||
# default -- this is the local/recipes/ counterpart of the local/sources/
|
||||
# dirty gate in build-redbear.sh.
|
||||
baseline_dirty=0
|
||||
for f in "${MODIFIED[@]}"; do
|
||||
[ -z "$f" ] && continue
|
||||
[ -n "${known[$f]:-}" ] && baseline_dirty=$(( baseline_dirty + 1 ))
|
||||
done
|
||||
|
||||
if [ "$baseline_dirty" -gt 0 ]; then
|
||||
if [ "${REDBEAR_STRICT_TRACKED_SOURCES:-0}" = "1" ]; then
|
||||
echo ">>> ERROR: $baseline_dirty uncommitted change(s) in tracked source trees" \
|
||||
"(REDBEAR_STRICT_TRACKED_SOURCES=1)." >&2
|
||||
rc=1
|
||||
else
|
||||
say ">>> Preflight note: $baseline_dirty uncommitted change(s) in tracked vendored" \
|
||||
"source trees (baseline-known)."
|
||||
say " These live in a DURABLE tree but are not committed; a clean re-extract would" \
|
||||
"lose them. Commit them, or move them into a recipe sed / patch."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
say ">>> Preflight: tracked vendored source trees intact (no deletions, no unexplained edits)."
|
||||
fi
|
||||
exit "$rc"
|
||||
Reference in New Issue
Block a user