Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions host-setup/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ resolve_ref() {

# An unauthenticated request is rate limited per address, so a busy network can lose the lookup while the download itself is fine.
# The run continues and says it cannot name its own revision, which is worth a warning rather than a refusal.
# This fallback is deliberate and gates no mutation: download_tree falls back to fetching $REF by name when RESOLVED is empty, exactly as it would if resolve_ref did not exist, and it has its own die on a real download failure.
warn "Could not resolve $REF to a commit, so this run cannot be attributed to one"
RESOLVED=""
return 0
Expand Down
24 changes: 19 additions & 5 deletions host-setup/linux/install-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,13 @@ apt_install_displacing() {
return 0
fi

# The simulation is what removals are previewed from, so a simulation that fails to run at all must not read the same as a simulation that ran and found nothing to remove.
local sim
sim=$(apt-get -s install "$package" 2>&1) ||
die "apt-get -s install $package failed, so removals cannot be previewed before the real install runs: $sim"

local -a removals=()
readarray -t removals < <(apt-get -s install "$package" 2>/dev/null | awk '/^Remv / { print $2 }')
readarray -t removals < <(awk '/^Remv / { print $2 }' <<<"$sim")
if [[ ${#removals[@]} -gt 0 ]]; then
log " Installing $package removes ${#removals[@]} package(s): ${removals[*]}"
log " Their dependencies are left installed, for a later apt autoremove to clean up"
Expand Down Expand Up @@ -1410,11 +1415,20 @@ configure_sudo_timestamp() {
"${SUDO[@]}" cmp -s "$staged" "$SUDOERS_FILE" 2>/dev/null && own_current=true

# Another file setting either option is named rather than merged into, since which one wins is the order sudo reads them in and not something this can decide.
local elsewhere
local elsewhere status=0
# A name holding a dot or ending in a tilde is one sudo skips, this run's own staged file included, so a setting in it is an override sudo never reads.
elsewhere=$("${SUDO[@]}" grep -rnsE '^[[:space:]]*Defaults.*timestamp_(type|timeout)' \
--exclude='*.*' --exclude='*~' --exclude="${SUDOERS_FILE##*/}" \
/etc/sudoers /etc/sudoers.d 2>/dev/null) || elsewhere=""
# Grep's own "no match" exit (1) is folded to 0 inside the privileged shell, so the status sudo hands back distinguishes only "sudo could not even run this" from "the scan ran", never grep's ordinary no-match case from a sudo failure that also happens to exit 1.
# shellcheck disable=SC2016 # $1/$2 are meant for the inner sh -c script, not this outer shell.
elsewhere=$("${SUDO[@]}" sh -c '
out=$(grep -rnsE "$1" --exclude="*.*" --exclude="*~" --exclude="$2" /etc/sudoers /etc/sudoers.d 2>&1)
rc=$?
printf %s "$out"
[ "$rc" -eq 1 ] && exit 0
exit "$rc"
' _ '^[[:space:]]*Defaults.*timestamp_(type|timeout)' "${SUDOERS_FILE##*/}") || status=$?
if [[ $status -ne 0 ]]; then
die "Scanning /etc/sudoers and /etc/sudoers.d for other timestamp_type/timestamp_timeout entries failed: $elsewhere"
fi

# Only this user's own entry is ever a delete candidate; a different user's entry, or one with no user named at all, changes something beyond what this run was asked to change, so it is reported and left alone.
local -a delete_files=() unsafe_files=()
Expand Down
20 changes: 16 additions & 4 deletions host-setup/linux/upgrade-host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,15 @@ refresh_snaps() {
}

upgradable_count() {
apt list --upgradable 2>/dev/null | grep -c '/' || true
# A failed listing and a listing that genuinely found nothing upgradable both read as "no matches" through grep alone, so the two are told apart here rather than both printing 0.
# This only ever backs a status report, so the answer here is "unknown" rather than a die: nothing downstream mutates on the strength of this count.
local out status=0
out=$(apt list --upgradable 2>/dev/null) || status=$?
if [[ $status -ne 0 ]]; then
printf 'unknown, apt list --upgradable failed (exit %s)' "$status"
return 0
fi
printf '%s package(s), against the lists as they stand' "$(grep -c '/' <<<"$out" || true)"
}

# --- Reboot ---
Expand Down Expand Up @@ -241,8 +249,12 @@ release_preconditions() {
die "Held packages block a release upgrade, unhold them first: $held"
fi

local audit
audit=$("${SUDO[@]}" dpkg --audit 2>/dev/null || true)
# A dpkg --audit that fails to run is not the same as one that runs and finds nothing, and only the second one clears the way into a release upgrade.
local audit status=0
audit=$("${SUDO[@]}" dpkg --audit 2>&1) || status=$?
if [[ $status -ne 0 ]]; then
die "dpkg --audit failed to run (exit $status), so half-configured packages cannot be ruled out before a release upgrade: $audit"
fi
if [[ -n $audit ]]; then
die "dpkg reports half-configured packages, fix them first: $audit"
fi
Expand Down Expand Up @@ -547,7 +559,7 @@ release_summary() {

status() {
log "Host : $(host_description)"
log "Upgradable: $(upgradable_count) package(s), against the lists as they stand"
log "Upgradable: $(upgradable_count)"
log "Release : $(release_summary)"

report_reboot
Expand Down
Loading