Fix: Lock installer input state

This commit is contained in:
Gabriel Brown
2026-08-27 09:48:28 -04:00
parent 1ffd05f0e1
commit ba4e5e6677
5 changed files with 3239 additions and 54 deletions
+52 -21
View File
@@ -89,36 +89,56 @@ STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/panama"
PACKAGES_HASH="$STATE_DIR/packages-hash"
hash_packages() {
local file relative size
local file relative size fixed_input digest
{
find "$PANAMA_PATH/setup/packages" -maxdepth 1 -type f -print0
printf '%s\0' \
"$PANAMA_PATH/setup/scripts/install-packages" \
"$PANAMA_PATH/setup/lib/artifact-provenance"
find "$PANAMA_PATH/setup/provenance" -type f -print0
} | LC_ALL=C sort -z | while IFS= read -r -d '' file; do
relative="${file#"$PANAMA_PATH"/}"
size="$(wc -c <"$file")"
printf '%s\0%s\0' "$relative" "$size"
cat -- "$file"
printf '\0'
done | sha256sum | cut -d' ' -f1
for fixed_input in \
"$PANAMA_PATH/setup/scripts/install-packages" \
"$PANAMA_PATH/setup/lib/artifact-provenance"; do
[[ -f "$fixed_input" && ! -L "$fixed_input" && -r "$fixed_input" ]] || return 1
done
digest="$(
{
find "$PANAMA_PATH/setup/packages" -maxdepth 1 -type f -print0 || exit 1
printf '%s\0' \
"$PANAMA_PATH/setup/scripts/install-packages" \
"$PANAMA_PATH/setup/lib/artifact-provenance" || exit 1
find "$PANAMA_PATH/setup/provenance" -type f -print0 || exit 1
} | LC_ALL=C sort -z | while IFS= read -r -d '' file; do
relative="${file#"$PANAMA_PATH"/}"
size="$(wc -c <"$file")" || exit 1
printf '%s\0%s\0' "$relative" "$size" || exit 1
cat -- "$file" || exit 1
printf '\0' || exit 1
done | sha256sum | cut -d' ' -f1
)" || return 1
printf '%s\n' "$digest"
}
packages_needed() {
local current_hash recorded_hash
(( FORCE_PACKAGES )) && return 0
(( UPGRADE )) || return 0
[[ -r "$PACKAGES_HASH" ]] || return 0
[[ "$(hash_packages)" != "$(cat "$PACKAGES_HASH")" ]]
current_hash="$(hash_packages)" || return 2
recorded_hash="$(cat "$PACKAGES_HASH")" || return 2
[[ "$current_hash" != "$recorded_hash" ]]
}
# Written only after the stage succeeds, mirroring the rule panama-migrate
# documents for its markers: a step that did not complete has not happened, and
# recording it as done hides it forever.
record_packages_hash() {
local temporary_hash
mkdir -p "$STATE_DIR"
hash_packages >"$PACKAGES_HASH"
temporary_hash="$(mktemp "$STATE_DIR/.packages-hash.XXXXXX")" || return 1
if hash_packages >"$temporary_hash"; then
mv -f -- "$temporary_hash" "$PACKAGES_HASH"
else
rm -f -- "$temporary_hash"
return 1
fi
}
# Repository trust is checked before the installer can reach its bootstrap DNF.
@@ -322,14 +342,25 @@ for stage in "${STAGES[@]}"; do
script="$PANAMA_PATH/setup/scripts/$stage"
[[ -x "$script" ]] || continue
printf '\n=== %s ===\n' "$stage"
if [[ "$stage" == install-packages ]] && ! packages_needed; then
echo "The package lists have not changed since the last run; skipping."
echo "Run with --packages to install them anyway."
continue
if [[ "$stage" == install-packages ]]; then
package_state_status=0
packages_needed || package_state_status=$?
if (( package_state_status == 1 )); then
echo "The package lists have not changed since the last run; skipping."
echo "Run with --packages to install them anyway."
continue
elif (( package_state_status != 0 )); then
failed+=("$stage")
printf '!!! %s could not read its tracked installation inputs\n' "$stage" >&2
continue
fi
fi
if "$script"; then
if [[ "$stage" == install-packages ]]; then
record_packages_hash
if ! record_packages_hash; then
failed+=("$stage")
printf '!!! %s could not record its tracked installation inputs\n' "$stage" >&2
fi
fi
else
stage_status=$?